Make WordPress Core

Ticket #41810: formatting.php

File formatting.php, 226.0 KB (added by Mirucon, 8 years ago)
Line 
1<?php
2/**
3 * Main WordPress Formatting API.
4 *
5 * Handles many functions for formatting output.
6 *
7 * @package WordPress
8 */
9
10/**
11 * Replaces common plain text characters into formatted entities
12 *
13 * As an example,
14 *
15 *     'cause today's effort makes it worth tomorrow's "holiday" ...
16 *
17 * Becomes:
18 *
19 *     &#8217;cause today&#8217;s effort makes it worth tomorrow&#8217;s &#8220;holiday&#8221; &#8230;
20 *
21 * Code within certain html blocks are skipped.
22 *
23 * Do not use this function before the {@see 'init'} action hook; everything will break.
24 *
25 * @since 0.71
26 *
27 * @global array $wp_cockneyreplace Array of formatted entities for certain common phrases
28 * @global array $shortcode_tags
29 * @staticvar array  $static_characters
30 * @staticvar array  $static_replacements
31 * @staticvar array  $dynamic_characters
32 * @staticvar array  $dynamic_replacements
33 * @staticvar array  $default_no_texturize_tags
34 * @staticvar array  $default_no_texturize_shortcodes
35 * @staticvar bool   $run_texturize
36 * @staticvar string $apos
37 * @staticvar string $prime
38 * @staticvar string $double_prime
39 * @staticvar string $opening_quote
40 * @staticvar string $closing_quote
41 * @staticvar string $opening_single_quote
42 * @staticvar string $closing_single_quote
43 * @staticvar string $open_q_flag
44 * @staticvar string $open_sq_flag
45 * @staticvar string $apos_flag
46 *
47 * @param string $text The text to be formatted
48 * @param bool   $reset Set to true for unit testing. Translated patterns will reset.
49 * @return string The string replaced with html entities
50 */
51function wptexturize( $text, $reset = false ) {
52        global $wp_cockneyreplace, $shortcode_tags;
53        static $static_characters = null,
54                $static_replacements = null,
55                $dynamic_characters = null,
56                $dynamic_replacements = null,
57                $default_no_texturize_tags = null,
58                $default_no_texturize_shortcodes = null,
59                $run_texturize = true,
60                $apos = null,
61                $prime = null,
62                $double_prime = null,
63                $opening_quote = null,
64                $closing_quote = null,
65                $opening_single_quote = null,
66                $closing_single_quote = null,
67                $open_q_flag = '<!--oq-->',
68                $open_sq_flag = '<!--osq-->',
69                $apos_flag = '<!--apos-->';
70
71        // If there's nothing to do, just stop.
72        if ( empty( $text ) || false === $run_texturize ) {
73                return $text;
74        }
75
76        // Set up static variables. Run once only.
77        if ( $reset || ! isset( $static_characters ) ) {
78                /**
79                 * Filters whether to skip running wptexturize().
80                 *
81                 * Passing false to the filter will effectively short-circuit wptexturize().
82                 * returning the original text passed to the function instead.
83                 *
84                 * The filter runs only once, the first time wptexturize() is called.
85                 *
86                 * @since 4.0.0
87                 *
88                 * @see wptexturize()
89                 *
90                 * @param bool $run_texturize Whether to short-circuit wptexturize().
91                 */
92                $run_texturize = apply_filters( 'run_wptexturize', $run_texturize );
93                if ( false === $run_texturize ) {
94                        return $text;
95                }
96
97                /* translators: opening curly double quote */
98                $opening_quote = _x( '&#8220;', 'opening curly double quote' );
99                /* translators: closing curly double quote */
100                $closing_quote = _x( '&#8221;', 'closing curly double quote' );
101
102                /* translators: apostrophe, for example in 'cause or can't */
103                $apos = _x( '&#8217;', 'apostrophe' );
104
105                /* translators: prime, for example in 9' (nine feet) */
106                $prime = _x( '&#8242;', 'prime' );
107                /* translators: double prime, for example in 9" (nine inches) */
108                $double_prime = _x( '&#8243;', 'double prime' );
109
110                /* translators: opening curly single quote */
111                $opening_single_quote = _x( '&#8216;', 'opening curly single quote' );
112                /* translators: closing curly single quote */
113                $closing_single_quote = _x( '&#8217;', 'closing curly single quote' );
114
115                /* translators: en dash */
116                $en_dash = _x( '&#8211;', 'en dash' );
117                /* translators: em dash */
118                $em_dash = _x( '&#8212;', 'em dash' );
119
120                $default_no_texturize_tags = array('pre', 'code', 'kbd', 'style', 'script', 'tt');
121                $default_no_texturize_shortcodes = array('code');
122
123                // if a plugin has provided an autocorrect array, use it
124                if ( isset($wp_cockneyreplace) ) {
125                        $cockney = array_keys( $wp_cockneyreplace );
126                        $cockneyreplace = array_values( $wp_cockneyreplace );
127                } else {
128                        /* translators: This is a comma-separated list of words that defy the syntax of quotations in normal use,
129                         * for example...  'We do not have enough words yet' ... is a typical quoted phrase.  But when we write
130                         * lines of code 'til we have enough of 'em, then we need to insert apostrophes instead of quotes.
131                         */
132                        $cockney = explode( ',', _x( "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em",
133                                'Comma-separated list of words to texturize in your language' ) );
134
135                        $cockneyreplace = explode( ',', _x( '&#8217;tain&#8217;t,&#8217;twere,&#8217;twas,&#8217;tis,&#8217;twill,&#8217;til,&#8217;bout,&#8217;nuff,&#8217;round,&#8217;cause,&#8217;em',
136                                'Comma-separated list of replacement words in your language' ) );
137                }
138
139                $static_characters = array_merge( array( '...', '``', '\'\'', ' (tm)' ), $cockney );
140                $static_replacements = array_merge( array( '&#8230;', $opening_quote, $closing_quote, ' &#8482;' ), $cockneyreplace );
141
142
143                // Pattern-based replacements of characters.
144                // Sort the remaining patterns into several arrays for performance tuning.
145                $dynamic_characters = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
146                $dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() );
147                $dynamic = array();
148                $spaces = wp_spaces_regexp();
149
150                // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation.
151                if ( "'" !== $apos || "'" !== $closing_single_quote ) {
152                        $dynamic[ '/\'(\d\d)\'(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_single_quote;
153                }
154                if ( "'" !== $apos || '"' !== $closing_quote ) {
155                        $dynamic[ '/\'(\d\d)"(?=\Z|[.,:;!?)}\-\]]|&gt;|' . $spaces . ')/' ] = $apos_flag . '$1' . $closing_quote;
156                }
157
158                // '99 '99s '99's (apostrophe)  But never '9 or '99% or '999 or '99.0.
159                if ( "'" !== $apos ) {
160                        $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos_flag;
161                }
162
163                // Quoted Numbers like '0.42'
164                if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) {
165                        $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $open_sq_flag . '$1' . $closing_single_quote;
166                }
167
168                // Single quote at start, or preceded by (, {, <, [, ", -, or spaces.
169                if ( "'" !== $opening_single_quote ) {
170                        $dynamic[ '/(?<=\A|[([{"\-]|&lt;|' . $spaces . ')\'/' ] = $open_sq_flag;
171                }
172
173                // Apostrophe in a word.  No spaces, double apostrophes, or other punctuation.
174                if ( "'" !== $apos ) {
175                        $dynamic[ '/(?<!' . $spaces . ')\'(?!\Z|[.,:;!?"\'(){}[\]\-]|&[lg]t;|' . $spaces . ')/' ] = $apos_flag;
176                }
177
178                $dynamic_characters['apos'] = array_keys( $dynamic );
179                $dynamic_replacements['apos'] = array_values( $dynamic );
180                $dynamic = array();
181
182                // Quoted Numbers like "42"
183                if ( '"' !== $opening_quote && '"' !== $closing_quote ) {
184                        $dynamic[ '/(?<=\A|' . $spaces . ')"(\d[.,\d]*)"/' ] = $open_q_flag . '$1' . $closing_quote;
185                }
186
187                // Double quote at start, or preceded by (, {, <, [, -, or spaces, and not followed by spaces.
188                if ( '"' !== $opening_quote ) {
189                        $dynamic[ '/(?<=\A|[([{\-]|&lt;|' . $spaces . ')"(?!' . $spaces . ')/' ] = $open_q_flag;
190                }
191
192                $dynamic_characters['quote'] = array_keys( $dynamic );
193                $dynamic_replacements['quote'] = array_values( $dynamic );
194                $dynamic = array();
195
196                // Dashes and spaces
197                $dynamic[ '/---/' ] = $em_dash;
198                $dynamic[ '/(?<=^|' . $spaces . ')--(?=$|' . $spaces . ')/' ] = $em_dash;
199                $dynamic[ '/(?<!xn)--/' ] = $en_dash;
200                $dynamic[ '/(?<=^|' . $spaces . ')-(?=$|' . $spaces . ')/' ] = $en_dash;
201
202                $dynamic_characters['dash'] = array_keys( $dynamic );
203                $dynamic_replacements['dash'] = array_values( $dynamic );
204        }
205
206        // Must do this every time in case plugins use these filters in a context sensitive manner
207        /**
208         * Filters the list of HTML elements not to texturize.
209         *
210         * @since 2.8.0
211         *
212         * @param array $default_no_texturize_tags An array of HTML element names.
213         */
214        $no_texturize_tags = apply_filters( 'no_texturize_tags', $default_no_texturize_tags );
215        /**
216         * Filters the list of shortcodes not to texturize.
217         *
218         * @since 2.8.0
219         *
220         * @param array $default_no_texturize_shortcodes An array of shortcode names.
221         */
222        $no_texturize_shortcodes = apply_filters( 'no_texturize_shortcodes', $default_no_texturize_shortcodes );
223
224        $no_texturize_tags_stack = array();
225        $no_texturize_shortcodes_stack = array();
226
227        // Look for shortcodes and HTML elements.
228
229        preg_match_all( '@\[/?([^<>&/\[\]\x00-\x20=]++)@', $text, $matches );
230        $tagnames = array_intersect( array_keys( $shortcode_tags ), $matches[1] );
231        $found_shortcodes = ! empty( $tagnames );
232        $shortcode_regex = $found_shortcodes ? _get_wptexturize_shortcode_regex( $tagnames ) : '';
233        $regex = _get_wptexturize_split_regex( $shortcode_regex );
234
235        $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
236
237        foreach ( $textarr as &$curl ) {
238                // Only call _wptexturize_pushpop_element if $curl is a delimiter.
239                $first = $curl[0];
240                if ( '<' === $first ) {
241                        if ( '<!--' === substr( $curl, 0, 4 ) ) {
242                                // This is an HTML comment delimiter.
243                                continue;
244                        } else {
245                                // This is an HTML element delimiter.
246
247                                // Replace each & with &#038; unless it already looks like an entity.
248                                $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&#038;', $curl );
249
250                                _wptexturize_pushpop_element( $curl, $no_texturize_tags_stack, $no_texturize_tags );
251                        }
252
253                } elseif ( '' === trim( $curl ) ) {
254                        // This is a newline between delimiters.  Performance improves when we check this.
255                        continue;
256
257                } elseif ( '[' === $first && $found_shortcodes && 1 === preg_match( '/^' . $shortcode_regex . '$/', $curl ) ) {
258                        // This is a shortcode delimiter.
259
260                        if ( '[[' !== substr( $curl, 0, 2 ) && ']]' !== substr( $curl, -2 ) ) {
261                                // Looks like a normal shortcode.
262                                _wptexturize_pushpop_element( $curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes );
263                        } else {
264                                // Looks like an escaped shortcode.
265                                continue;
266                        }
267
268                } elseif ( empty( $no_texturize_shortcodes_stack ) && empty( $no_texturize_tags_stack ) ) {
269                        // This is neither a delimiter, nor is this content inside of no_texturize pairs.  Do texturize.
270
271                        $curl = str_replace( $static_characters, $static_replacements, $curl );
272
273                        if ( false !== strpos( $curl, "'" ) ) {
274                                $curl = preg_replace( $dynamic_characters['apos'], $dynamic_replacements['apos'], $curl );
275                                $curl = wptexturize_primes( $curl, "'", $prime, $open_sq_flag, $closing_single_quote );
276                                $curl = str_replace( $apos_flag, $apos, $curl );
277                                $curl = str_replace( $open_sq_flag, $opening_single_quote, $curl );
278                        }
279                        if ( false !== strpos( $curl, '"' ) ) {
280                                $curl = preg_replace( $dynamic_characters['quote'], $dynamic_replacements['quote'], $curl );
281                                $curl = wptexturize_primes( $curl, '"', $double_prime, $open_q_flag, $closing_quote );
282                                $curl = str_replace( $open_q_flag, $opening_quote, $curl );
283                        }
284                        if ( false !== strpos( $curl, '-' ) ) {
285                                $curl = preg_replace( $dynamic_characters['dash'], $dynamic_replacements['dash'], $curl );
286                        }
287
288                        // 9x9 (times), but never 0x9999
289                        if ( 1 === preg_match( '/(?<=\d)x\d/', $curl ) ) {
290                                // Searching for a digit is 10 times more expensive than for the x, so we avoid doing this one!
291                                $curl = preg_replace( '/\b(\d(?(?<=0)[\d\.,]+|[\d\.,]*))x(\d[\d\.,]*)\b/', '$1&#215;$2', $curl );
292                        }
293
294                        // Replace each & with &#038; unless it already looks like an entity.
295                        $curl = preg_replace( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', '&#038;', $curl );
296                }
297        }
298
299        return implode( '', $textarr );
300}
301
302/**
303 * Implements a logic tree to determine whether or not "7'." represents seven feet,
304 * then converts the special char into either a prime char or a closing quote char.
305 *
306 * @since 4.3.0
307 *
308 * @param string $haystack    The plain text to be searched.
309 * @param string $needle      The character to search for such as ' or ".
310 * @param string $prime       The prime char to use for replacement.
311 * @param string $open_quote  The opening quote char. Opening quote replacement must be
312 *                            accomplished already.
313 * @param string $close_quote The closing quote char to use for replacement.
314 * @return string The $haystack value after primes and quotes replacements.
315 */
316function wptexturize_primes( $haystack, $needle, $prime, $open_quote, $close_quote ) {
317        $spaces = wp_spaces_regexp();
318        $flag = '<!--wp-prime-or-quote-->';
319        $quote_pattern = "/$needle(?=\\Z|[.,:;!?)}\\-\\]]|&gt;|" . $spaces . ")/";
320        $prime_pattern    = "/(?<=\\d)$needle/";
321        $flag_after_digit = "/(?<=\\d)$flag/";
322        $flag_no_digit    = "/(?<!\\d)$flag/";
323
324        $sentences = explode( $open_quote, $haystack );
325
326        foreach ( $sentences as $key => &$sentence ) {
327                if ( false === strpos( $sentence, $needle ) ) {
328                        continue;
329                } elseif ( 0 !== $key && 0 === substr_count( $sentence, $close_quote ) ) {
330                        $sentence = preg_replace( $quote_pattern, $flag, $sentence, -1, $count );
331                        if ( $count > 1 ) {
332                                // This sentence appears to have multiple closing quotes.  Attempt Vulcan logic.
333                                $sentence = preg_replace( $flag_no_digit, $close_quote, $sentence, -1, $count2 );
334                                if ( 0 === $count2 ) {
335                                        // Try looking for a quote followed by a period.
336                                        $count2 = substr_count( $sentence, "$flag." );
337                                        if ( $count2 > 0 ) {
338                                                // Assume the rightmost quote-period match is the end of quotation.
339                                                $pos = strrpos( $sentence, "$flag." );
340                                        } else {
341                                                // When all else fails, make the rightmost candidate a closing quote.
342                                                // This is most likely to be problematic in the context of bug #18549.
343                                                $pos = strrpos( $sentence, $flag );
344                                        }
345                                        $sentence = substr_replace( $sentence, $close_quote, $pos, strlen( $flag ) );
346                                }
347                                // Use conventional replacement on any remaining primes and quotes.
348                                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
349                                $sentence = preg_replace( $flag_after_digit, $prime, $sentence );
350                                $sentence = str_replace( $flag, $close_quote, $sentence );
351                        } elseif ( 1 == $count ) {
352                                // Found only one closing quote candidate, so give it priority over primes.
353                                $sentence = str_replace( $flag, $close_quote, $sentence );
354                                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
355                        } else {
356                                // No closing quotes found.  Just run primes pattern.
357                                $sentence = preg_replace( $prime_pattern, $prime, $sentence );
358                        }
359                } else {
360                        $sentence = preg_replace( $prime_pattern, $prime, $sentence );
361                        $sentence = preg_replace( $quote_pattern, $close_quote, $sentence );
362                }
363                if ( '"' == $needle && false !== strpos( $sentence, '"' ) ) {
364                        $sentence = str_replace( '"', $close_quote, $sentence );
365                }
366        }
367
368        return implode( $open_quote, $sentences );
369}
370
371/**
372 * Search for disabled element tags. Push element to stack on tag open and pop
373 * on tag close.
374 *
375 * Assumes first char of $text is tag opening and last char is tag closing.
376 * Assumes second char of $text is optionally '/' to indicate closing as in </html>.
377 *
378 * @since 2.9.0
379 * @access private
380 *
381 * @param string $text Text to check. Must be a tag like `<html>` or `[shortcode]`.
382 * @param array  $stack List of open tag elements.
383 * @param array  $disabled_elements The tag names to match against. Spaces are not allowed in tag names.
384 */
385function _wptexturize_pushpop_element( $text, &$stack, $disabled_elements ) {
386        // Is it an opening tag or closing tag?
387        if ( isset( $text[1] ) && '/' !== $text[1] ) {
388                $opening_tag = true;
389                $name_offset = 1;
390        } elseif ( 0 == count( $stack ) ) {
391                // Stack is empty. Just stop.
392                return;
393        } else {
394                $opening_tag = false;
395                $name_offset = 2;
396        }
397
398        // Parse out the tag name.
399        $space = strpos( $text, ' ' );
400        if ( false === $space ) {
401                $space = -1;
402        } else {
403                $space -= $name_offset;
404        }
405        $tag = substr( $text, $name_offset, $space );
406
407        // Handle disabled tags.
408        if ( in_array( $tag, $disabled_elements ) ) {
409                if ( $opening_tag ) {
410                        /*
411                         * This disables texturize until we find a closing tag of our type
412                         * (e.g. <pre>) even if there was invalid nesting before that
413                         *
414                         * Example: in the case <pre>sadsadasd</code>"baba"</pre>
415                         *          "baba" won't be texturize
416                         */
417
418                        array_push( $stack, $tag );
419                } elseif ( end( $stack ) == $tag ) {
420                        array_pop( $stack );
421                }
422        }
423}
424
425/**
426 * Replaces double line-breaks with paragraph elements.
427 *
428 * A group of regex replaces used to identify text formatted with newlines and
429 * replace double line-breaks with HTML paragraph tags. The remaining line-breaks
430 * after conversion become <<br />> tags, unless $br is set to '0' or 'false'.
431 *
432 * @since 0.71
433 *
434 * @param string $pee The text which has to be formatted.
435 * @param bool   $br  Optional. If set, this will convert all remaining line-breaks
436 *                    after paragraphing. Default true.
437 * @return string Text which has been converted into correct paragraph tags.
438 */
439function wpautop( $pee, $br = true ) {
440        $pre_tags = array();
441
442        if ( trim($pee) === '' )
443                return '';
444
445        // Just to make things a little easier, pad the end.
446        $pee = $pee . "\n";
447
448        /*
449         * Pre tags shouldn't be touched by autop.
450         * Replace pre tags with placeholders and bring them back after autop.
451         */
452        if ( strpos($pee, '<pre') !== false ) {
453                $pee_parts = explode( '</pre>', $pee );
454                $last_pee = array_pop($pee_parts);
455                $pee = '';
456                $i = 0;
457
458                foreach ( $pee_parts as $pee_part ) {
459                        $start = strpos($pee_part, '<pre');
460
461                        // Malformed html?
462                        if ( $start === false ) {
463                                $pee .= $pee_part;
464                                continue;
465                        }
466
467                        $name = "<pre wp-pre-tag-$i></pre>";
468                        $pre_tags[$name] = substr( $pee_part, $start ) . '</pre>';
469
470                        $pee .= substr( $pee_part, 0, $start ) . $name;
471                        $i++;
472                }
473
474                $pee .= $last_pee;
475        }
476        // Change multiple <br>s into two line breaks, which will turn into paragraphs.
477        $pee = preg_replace('|<br\s*/?>\s*<br\s*/?>|', "\n\n", $pee);
478
479        $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
480
481        // Add a double line break above block-level opening tags.
482        $pee = preg_replace('!(<' . $allblocks . '[\s/>])!', "\n\n$1", $pee);
483
484        // Add a double line break below block-level closing tags.
485        $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
486
487        // Standardize newline characters to "\n".
488        $pee = str_replace(array("\r\n", "\r"), "\n", $pee);
489
490        // Find newlines in all elements and add placeholders.
491        $pee = wp_replace_in_html_tags( $pee, array( "\n" => " <!-- wpnl --> " ) );
492
493        // Collapse line breaks before and after <option> elements so they don't get autop'd.
494        if ( strpos( $pee, '<option' ) !== false ) {
495                $pee = preg_replace( '|\s*<option|', '<option', $pee );
496                $pee = preg_replace( '|</option>\s*|', '</option>', $pee );
497        }
498
499        /*
500         * Collapse line breaks inside <object> elements, before <param> and <embed> elements
501         * so they don't get autop'd.
502         */
503        if ( strpos( $pee, '</object>' ) !== false ) {
504                $pee = preg_replace( '|(<object[^>]*>)\s*|', '$1', $pee );
505                $pee = preg_replace( '|\s*</object>|', '</object>', $pee );
506                $pee = preg_replace( '%\s*(</?(?:param|embed)[^>]*>)\s*%', '$1', $pee );
507        }
508
509        /*
510         * Collapse line breaks inside <audio> and <video> elements,
511         * before and after <source> and <track> elements.
512         */
513        if ( strpos( $pee, '<source' ) !== false || strpos( $pee, '<track' ) !== false ) {
514                $pee = preg_replace( '%([<\[](?:audio|video)[^>\]]*[>\]])\s*%', '$1', $pee );
515                $pee = preg_replace( '%\s*([<\[]/(?:audio|video)[>\]])%', '$1', $pee );
516                $pee = preg_replace( '%\s*(<(?:source|track)[^>]*>)\s*%', '$1', $pee );
517        }
518
519        // Collapse line breaks before and after <figcaption> elements.
520        if ( strpos( $pee, '<figcaption' ) !== false ) {
521                $pee = preg_replace( '|\s*(<figcaption[^>]*>)|', '$1', $pee );
522                $pee = preg_replace( '|</figcaption>\s*|', '</figcaption>', $pee );
523        }
524
525        // Remove more than two contiguous line breaks.
526        $pee = preg_replace("/\n\n+/", "\n\n", $pee);
527
528        // Split up the contents into an array of strings, separated by double line breaks.
529        $pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
530
531        // Reset $pee prior to rebuilding.
532        $pee = '';
533
534        // Rebuild the content as a string, wrapping every bit with a <p>.
535        foreach ( $pees as $tinkle ) {
536                $pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
537        }
538
539        // Under certain strange conditions it could create a P of entirely whitespace.
540        $pee = preg_replace('|<p>\s*</p>|', '', $pee);
541
542        // Add a closing <p> inside <div>, <address>, or <form> tag if missing.
543        $pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
544
545        // If an opening or closing block element tag is wrapped in a <p>, unwrap it.
546        $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
547
548        // In some cases <li> may get wrapped in <p>, fix them.
549        $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee);
550
551        // If a <blockquote> is wrapped with a <p>, move it inside the <blockquote>.
552        $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
553        $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
554
555        // If an opening or closing block element tag is preceded by an opening <p> tag, remove it.
556        $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
557
558        // If an opening or closing block element tag is followed by a closing <p> tag, remove it.
559        $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
560
561        // Optionally insert line breaks.
562        if ( $br ) {
563                // Replace newlines that shouldn't be touched with a placeholder.
564                $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', '_autop_newline_preservation_helper', $pee);
565
566                // Normalize <br>
567                $pee = str_replace( array( '<br>', '<br/>' ), '<br />', $pee );
568
569                // Replace any new line characters that aren't preceded by a <br /> with a <br />.
570                $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee);
571
572                // Replace newline placeholders with newlines.
573                $pee = str_replace('<WPPreserveNewline />', "\n", $pee);
574        }
575
576        // If a <br /> tag is after an opening or closing block tag, remove it.
577        $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
578
579        // If a <br /> tag is before a subset of opening or closing block tags, remove it.
580        $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
581        $pee = preg_replace( "|\n</p>$|", '</p>', $pee );
582
583        // Replace placeholder <pre> tags with their original content.
584        if ( !empty($pre_tags) )
585                $pee = str_replace(array_keys($pre_tags), array_values($pre_tags), $pee);
586
587        // Restore newlines in all elements.
588        if ( false !== strpos( $pee, '<!-- wpnl -->' ) ) {
589                $pee = str_replace( array( ' <!-- wpnl --> ', '<!-- wpnl -->' ), "\n", $pee );
590        }
591
592        return $pee;
593}
594
595/**
596 * Separate HTML elements and comments from the text.
597 *
598 * @since 4.2.4
599 *
600 * @param string $input The text which has to be formatted.
601 * @return array The formatted text.
602 */
603function wp_html_split( $input ) {
604        return preg_split( get_html_split_regex(), $input, -1, PREG_SPLIT_DELIM_CAPTURE );
605}
606
607/**
608 * Retrieve the regular expression for an HTML element.
609 *
610 * @since 4.4.0
611 *
612 * @staticvar string $regex
613 *
614 * @return string The regular expression
615 */
616function get_html_split_regex() {
617        static $regex;
618
619        if ( ! isset( $regex ) ) {
620                $comments =
621                          '!'           // Start of comment, after the <.
622                        . '(?:'         // Unroll the loop: Consume everything until --> is found.
623                        .     '-(?!->)' // Dash not followed by end of comment.
624                        .     '[^\-]*+' // Consume non-dashes.
625                        . ')*+'         // Loop possessively.
626                        . '(?:-->)?';   // End of comment. If not found, match all input.
627
628                $cdata =
629                          '!\[CDATA\['  // Start of comment, after the <.
630                        . '[^\]]*+'     // Consume non-].
631                        . '(?:'         // Unroll the loop: Consume everything until ]]> is found.
632                        .     '](?!]>)' // One ] not followed by end of comment.
633                        .     '[^\]]*+' // Consume non-].
634                        . ')*+'         // Loop possessively.
635                        . '(?:]]>)?';   // End of comment. If not found, match all input.
636
637                $escaped =
638                          '(?='           // Is the element escaped?
639                        .    '!--'
640                        . '|'
641                        .    '!\[CDATA\['
642                        . ')'
643                        . '(?(?=!-)'      // If yes, which type?
644                        .     $comments
645                        . '|'
646                        .     $cdata
647                        . ')';
648
649                $regex =
650                          '/('              // Capture the entire match.
651                        .     '<'           // Find start of element.
652                        .     '(?'          // Conditional expression follows.
653                        .         $escaped  // Find end of escaped element.
654                        .     '|'           // ... else ...
655                        .         '[^>]*>?' // Find end of normal element.
656                        .     ')'
657                        . ')/';
658        }
659
660        return $regex;
661}
662
663/**
664 * Retrieve the combined regular expression for HTML and shortcodes.
665 *
666 * @access private
667 * @ignore
668 * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap.
669 * @since 4.4.0
670 *
671 * @staticvar string $html_regex
672 *
673 * @param string $shortcode_regex The result from _get_wptexturize_shortcode_regex().  Optional.
674 * @return string The regular expression
675 */
676function _get_wptexturize_split_regex( $shortcode_regex = '' ) {
677        static $html_regex;
678
679        if ( ! isset( $html_regex ) ) {
680                $comment_regex =
681                          '!'           // Start of comment, after the <.
682                        . '(?:'         // Unroll the loop: Consume everything until --> is found.
683                        .     '-(?!->)' // Dash not followed by end of comment.
684                        .     '[^\-]*+' // Consume non-dashes.
685                        . ')*+'         // Loop possessively.
686                        . '(?:-->)?';   // End of comment. If not found, match all input.
687
688                $html_regex =                    // Needs replaced with wp_html_split() per Shortcode API Roadmap.
689                          '<'                // Find start of element.
690                        . '(?(?=!--)'        // Is this a comment?
691                        .     $comment_regex // Find end of comment.
692                        . '|'
693                        .     '[^>]*>?'      // Find end of element. If not found, match all input.
694                        . ')';
695        }
696
697        if ( empty( $shortcode_regex ) ) {
698                $regex = '/(' . $html_regex . ')/';
699        } else {
700                $regex = '/(' . $html_regex . '|' . $shortcode_regex . ')/';
701        }
702
703        return $regex;
704}
705
706/**
707 * Retrieve the regular expression for shortcodes.
708 *
709 * @access private
710 * @ignore
711 * @internal This function will be removed in 4.5.0 per Shortcode API Roadmap.
712 * @since 4.4.0
713 *
714 * @param array $tagnames List of shortcodes to find.
715 * @return string The regular expression
716 */
717function _get_wptexturize_shortcode_regex( $tagnames ) {
718        $tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) );
719        $tagregexp = "(?:$tagregexp)(?=[\\s\\]\\/])"; // Excerpt of get_shortcode_regex().
720        $regex =
721                  '\['              // Find start of shortcode.
722                . '[\/\[]?'         // Shortcodes may begin with [/ or [[
723                . $tagregexp        // Only match registered shortcodes, because performance.
724                . '(?:'
725                .     '[^\[\]<>]+'  // Shortcodes do not contain other shortcodes. Quantifier critical.
726                . '|'
727                .     '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >.
728                . ')*+'             // Possessive critical.
729                . '\]'              // Find end of shortcode.
730                . '\]?';            // Shortcodes may end with ]]
731
732        return $regex;
733}
734
735/**
736 * Replace characters or phrases within HTML elements only.
737 *
738 * @since 4.2.3
739 *
740 * @param string $haystack The text which has to be formatted.
741 * @param array $replace_pairs In the form array('from' => 'to', ...).
742 * @return string The formatted text.
743 */
744function wp_replace_in_html_tags( $haystack, $replace_pairs ) {
745        // Find all elements.
746        $textarr = wp_html_split( $haystack );
747        $changed = false;
748
749        // Optimize when searching for one item.
750        if ( 1 === count( $replace_pairs ) ) {
751                // Extract $needle and $replace.
752                foreach ( $replace_pairs as $needle => $replace );
753
754                // Loop through delimiters (elements) only.
755                for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
756                        if ( false !== strpos( $textarr[$i], $needle ) ) {
757                                $textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
758                                $changed = true;
759                        }
760                }
761        } else {
762                // Extract all $needles.
763                $needles = array_keys( $replace_pairs );
764
765                // Loop through delimiters (elements) only.
766                for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
767                        foreach ( $needles as $needle ) {
768                                if ( false !== strpos( $textarr[$i], $needle ) ) {
769                                        $textarr[$i] = strtr( $textarr[$i], $replace_pairs );
770                                        $changed = true;
771                                        // After one strtr() break out of the foreach loop and look at next element.
772                                        break;
773                                }
774                        }
775                }
776        }
777
778        if ( $changed ) {
779                $haystack = implode( $textarr );
780        }
781
782        return $haystack;
783}
784
785/**
786 * Newline preservation help function for wpautop
787 *
788 * @since 3.1.0
789 * @access private
790 *
791 * @param array $matches preg_replace_callback matches array
792 * @return string
793 */
794function _autop_newline_preservation_helper( $matches ) {
795        return str_replace( "\n", "<WPPreserveNewline />", $matches[0] );
796}
797
798/**
799 * Don't auto-p wrap shortcodes that stand alone
800 *
801 * Ensures that shortcodes are not wrapped in `<p>...</p>`.
802 *
803 * @since 2.9.0
804 *
805 * @global array $shortcode_tags
806 *
807 * @param string $pee The content.
808 * @return string The filtered content.
809 */
810function shortcode_unautop( $pee ) {
811        global $shortcode_tags;
812
813        if ( empty( $shortcode_tags ) || !is_array( $shortcode_tags ) ) {
814                return $pee;
815        }
816
817        $tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
818        $spaces = wp_spaces_regexp();
819
820        $pattern =
821                  '/'
822                . '<p>'                              // Opening paragraph
823                . '(?:' . $spaces . ')*+'            // Optional leading whitespace
824                . '('                                // 1: The shortcode
825                .     '\\['                          // Opening bracket
826                .     "($tagregexp)"                 // 2: Shortcode name
827                .     '(?![\\w-])'                   // Not followed by word character or hyphen
828                                                     // Unroll the loop: Inside the opening shortcode tag
829                .     '[^\\]\\/]*'                   // Not a closing bracket or forward slash
830                .     '(?:'
831                .         '\\/(?!\\])'               // A forward slash not followed by a closing bracket
832                .         '[^\\]\\/]*'               // Not a closing bracket or forward slash
833                .     ')*?'
834                .     '(?:'
835                .         '\\/\\]'                   // Self closing tag and closing bracket
836                .     '|'
837                .         '\\]'                      // Closing bracket
838                .         '(?:'                      // Unroll the loop: Optionally, anything between the opening and closing shortcode tags
839                .             '[^\\[]*+'             // Not an opening bracket
840                .             '(?:'
841                .                 '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
842                .                 '[^\\[]*+'         // Not an opening bracket
843                .             ')*+'
844                .             '\\[\\/\\2\\]'         // Closing shortcode tag
845                .         ')?'
846                .     ')'
847                . ')'
848                . '(?:' . $spaces . ')*+'            // optional trailing whitespace
849                . '<\\/p>'                           // closing paragraph
850                . '/';
851
852        return preg_replace( $pattern, '$1', $pee );
853}
854
855/**
856 * Checks to see if a string is utf8 encoded.
857 *
858 * NOTE: This function checks for 5-Byte sequences, UTF8
859 *       has Bytes Sequences with a maximum length of 4.
860 *
861 * @author bmorel at ssi dot fr (modified)
862 * @since 1.2.1
863 *
864 * @param string $str The string to be checked
865 * @return bool True if $str fits a UTF-8 model, false otherwise.
866 */
867function seems_utf8( $str ) {
868        mbstring_binary_safe_encoding();
869        $length = strlen($str);
870        reset_mbstring_encoding();
871        for ($i=0; $i < $length; $i++) {
872                $c = ord($str[$i]);
873                if ($c < 0x80) $n = 0; // 0bbbbbbb
874                elseif (($c & 0xE0) == 0xC0) $n=1; // 110bbbbb
875                elseif (($c & 0xF0) == 0xE0) $n=2; // 1110bbbb
876                elseif (($c & 0xF8) == 0xF0) $n=3; // 11110bbb
877                elseif (($c & 0xFC) == 0xF8) $n=4; // 111110bb
878                elseif (($c & 0xFE) == 0xFC) $n=5; // 1111110b
879                else return false; // Does not match any model
880                for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
881                        if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
882                                return false;
883                }
884        }
885        return true;
886}
887
888/**
889 * Converts a number of special characters into their HTML entities.
890 *
891 * Specifically deals with: &, <, >, ", and '.
892 *
893 * $quote_style can be set to ENT_COMPAT to encode " to
894 * &quot;, or ENT_QUOTES to do both. Default is ENT_NOQUOTES where no quotes are encoded.
895 *
896 * @since 1.2.2
897 * @access private
898 *
899 * @staticvar string $_charset
900 *
901 * @param string     $string         The text which is to be encoded.
902 * @param int|string $quote_style    Optional. Converts double quotes if set to ENT_COMPAT,
903 *                                   both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES.
904 *                                   Also compatible with old values; converting single quotes if set to 'single',
905 *                                   double if set to 'double' or both if otherwise set.
906 *                                   Default is ENT_NOQUOTES.
907 * @param string     $charset        Optional. The character encoding of the string. Default is false.
908 * @param bool       $double_encode  Optional. Whether to encode existing html entities. Default is false.
909 * @return string The encoded text with HTML entities.
910 */
911function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) {
912        $string = (string) $string;
913
914        if ( 0 === strlen( $string ) )
915                return '';
916
917        // Don't bother if there are no specialchars - saves some processing
918        if ( ! preg_match( '/[&<>"\']/', $string ) )
919                return $string;
920
921        // Account for the previous behaviour of the function when the $quote_style is not an accepted value
922        if ( empty( $quote_style ) )
923                $quote_style = ENT_NOQUOTES;
924        elseif ( ! in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) )
925                $quote_style = ENT_QUOTES;
926
927        // Store the site charset as a static to avoid multiple calls to wp_load_alloptions()
928        if ( ! $charset ) {
929                static $_charset = null;
930                if ( ! isset( $_charset ) ) {
931                        $alloptions = wp_load_alloptions();
932                        $_charset = isset( $alloptions['blog_charset'] ) ? $alloptions['blog_charset'] : '';
933                }
934                $charset = $_charset;
935        }
936
937        if ( in_array( $charset, array( 'utf8', 'utf-8', 'UTF8' ) ) )
938                $charset = 'UTF-8';
939
940        $_quote_style = $quote_style;
941
942        if ( $quote_style === 'double' ) {
943                $quote_style = ENT_COMPAT;
944                $_quote_style = ENT_COMPAT;
945        } elseif ( $quote_style === 'single' ) {
946                $quote_style = ENT_NOQUOTES;
947        }
948
949        if ( ! $double_encode ) {
950                // Guarantee every &entity; is valid, convert &garbage; into &amp;garbage;
951                // This is required for PHP < 5.4.0 because ENT_HTML401 flag is unavailable.
952                $string = wp_kses_normalize_entities( $string );
953        }
954
955        $string = @htmlspecialchars( $string, $quote_style, $charset, $double_encode );
956
957        // Back-compat.
958        if ( 'single' === $_quote_style )
959                $string = str_replace( "'", '&#039;', $string );
960
961        return $string;
962}
963
964/**
965 * Converts a number of HTML entities into their special characters.
966 *
967 * Specifically deals with: &, <, >, ", and '.
968 *
969 * $quote_style can be set to ENT_COMPAT to decode " entities,
970 * or ENT_QUOTES to do both " and '. Default is ENT_NOQUOTES where no quotes are decoded.
971 *
972 * @since 2.8.0
973 *
974 * @param string     $string The text which is to be decoded.
975 * @param string|int $quote_style Optional. Converts double quotes if set to ENT_COMPAT,
976 *                                both single and double if set to ENT_QUOTES or
977 *                                none if set to ENT_NOQUOTES.
978 *                                Also compatible with old _wp_specialchars() values;
979 *                                converting single quotes if set to 'single',
980 *                                double if set to 'double' or both if otherwise set.
981 *                                Default is ENT_NOQUOTES.
982 * @return string The decoded text without HTML entities.
983 */
984function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) {
985        $string = (string) $string;
986
987        if ( 0 === strlen( $string ) ) {
988                return '';
989        }
990
991        // Don't bother if there are no entities - saves a lot of processing
992        if ( strpos( $string, '&' ) === false ) {
993                return $string;
994        }
995
996        // Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value
997        if ( empty( $quote_style ) ) {
998                $quote_style = ENT_NOQUOTES;
999        } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) {
1000                $quote_style = ENT_QUOTES;
1001        }
1002
1003        // More complete than get_html_translation_table( HTML_SPECIALCHARS )
1004        $single = array( '&#039;'  => '\'', '&#x27;' => '\'' );
1005        $single_preg = array( '/&#0*39;/'  => '&#039;', '/&#x0*27;/i' => '&#x27;' );
1006        $double = array( '&quot;' => '"', '&#034;'  => '"', '&#x22;' => '"' );
1007        $double_preg = array( '/&#0*34;/'  => '&#034;', '/&#x0*22;/i' => '&#x22;' );
1008        $others = array( '&lt;'   => '<', '&#060;'  => '<', '&gt;'   => '>', '&#062;'  => '>', '&amp;'  => '&', '&#038;'  => '&', '&#x26;' => '&' );
1009        $others_preg = array( '/&#0*60;/'  => '&#060;', '/&#0*62;/'  => '&#062;', '/&#0*38;/'  => '&#038;', '/&#x0*26;/i' => '&#x26;' );
1010
1011        if ( $quote_style === ENT_QUOTES ) {
1012                $translation = array_merge( $single, $double, $others );
1013                $translation_preg = array_merge( $single_preg, $double_preg, $others_preg );
1014        } elseif ( $quote_style === ENT_COMPAT || $quote_style === 'double' ) {
1015                $translation = array_merge( $double, $others );
1016                $translation_preg = array_merge( $double_preg, $others_preg );
1017        } elseif ( $quote_style === 'single' ) {
1018                $translation = array_merge( $single, $others );
1019                $translation_preg = array_merge( $single_preg, $others_preg );
1020        } elseif ( $quote_style === ENT_NOQUOTES ) {
1021                $translation = $others;
1022                $translation_preg = $others_preg;
1023        }
1024
1025        // Remove zero padding on numeric entities
1026        $string = preg_replace( array_keys( $translation_preg ), array_values( $translation_preg ), $string );
1027
1028        // Replace characters according to translation table
1029        return strtr( $string, $translation );
1030}
1031
1032/**
1033 * Checks for invalid UTF8 in a string.
1034 *
1035 * @since 2.8.0
1036 *
1037 * @staticvar bool $is_utf8
1038 * @staticvar bool $utf8_pcre
1039 *
1040 * @param string  $string The text which is to be checked.
1041 * @param bool    $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
1042 * @return string The checked text.
1043 */
1044function wp_check_invalid_utf8( $string, $strip = false ) {
1045        $string = (string) $string;
1046
1047        if ( 0 === strlen( $string ) ) {
1048                return '';
1049        }
1050
1051        // Store the site charset as a static to avoid multiple calls to get_option()
1052        static $is_utf8 = null;
1053        if ( ! isset( $is_utf8 ) ) {
1054                $is_utf8 = in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) );
1055        }
1056        if ( ! $is_utf8 ) {
1057                return $string;
1058        }
1059
1060        // Check for support for utf8 in the installed PCRE library once and store the result in a static
1061        static $utf8_pcre = null;
1062        if ( ! isset( $utf8_pcre ) ) {
1063                $utf8_pcre = @preg_match( '/^./u', 'a' );
1064        }
1065        // We can't demand utf8 in the PCRE installation, so just return the string in those cases
1066        if ( !$utf8_pcre ) {
1067                return $string;
1068        }
1069
1070        // preg_match fails when it encounters invalid UTF8 in $string
1071        if ( 1 === @preg_match( '/^./us', $string ) ) {
1072                return $string;
1073        }
1074
1075        // Attempt to strip the bad chars if requested (not recommended)
1076        if ( $strip && function_exists( 'iconv' ) ) {
1077                return iconv( 'utf-8', 'utf-8', $string );
1078        }
1079
1080        return '';
1081}
1082
1083/**
1084 * Encode the Unicode values to be used in the URI.
1085 *
1086 * @since 1.5.0
1087 *
1088 * @param string $utf8_string
1089 * @param int    $length Max  length of the string
1090 * @return string String with Unicode encoded for URI.
1091 */
1092function utf8_uri_encode( $utf8_string, $length = 0 ) {
1093        $unicode = '';
1094        $values = array();
1095        $num_octets = 1;
1096        $unicode_length = 0;
1097
1098        mbstring_binary_safe_encoding();
1099        $string_length = strlen( $utf8_string );
1100        reset_mbstring_encoding();
1101
1102        for ($i = 0; $i < $string_length; $i++ ) {
1103
1104                $value = ord( $utf8_string[ $i ] );
1105
1106                if ( $value < 128 ) {
1107                        if ( $length && ( $unicode_length >= $length ) )
1108                                break;
1109                        $unicode .= chr($value);
1110                        $unicode_length++;
1111                } else {
1112                        if ( count( $values ) == 0 ) {
1113                                if ( $value < 224 ) {
1114                                        $num_octets = 2;
1115                                } elseif ( $value < 240 ) {
1116                                        $num_octets = 3;
1117                                } else {
1118                                        $num_octets = 4;
1119                                }
1120                        }
1121
1122                        $values[] = $value;
1123
1124                        if ( $length && ( $unicode_length + ($num_octets * 3) ) > $length )
1125                                break;
1126                        if ( count( $values ) == $num_octets ) {
1127                                for ( $j = 0; $j < $num_octets; $j++ ) {
1128                                        $unicode .= '%' . dechex( $values[ $j ] );
1129                                }
1130
1131                                $unicode_length += $num_octets * 3;
1132
1133                                $values = array();
1134                                $num_octets = 1;
1135                        }
1136                }
1137        }
1138
1139        return $unicode;
1140}
1141
1142/**
1143 * Converts all accent characters to ASCII characters.
1144 *
1145 * If there are no accent characters, then the string given is just returned.
1146 *
1147 * **Accent characters converted:**
1148 *
1149 * Currency signs:
1150 *
1151 * |   Code   | Glyph | Replacement |     Description     |
1152 * | -------- | ----- | ----------- | ------------------- |
1153 * | U+00A3   | £     | (empty)     | British Pound sign  |
1154 * | U+20AC   | €     | E           | Euro sign           |
1155 *
1156 * Decompositions for Latin-1 Supplement:
1157 *
1158 * |  Code   | Glyph | Replacement |               Description              |
1159 * | ------- | ----- | ----------- | -------------------------------------- |
1160 * | U+00AA  | ª     | a           | Feminine ordinal indicator             |
1161 * | U+00BA  | º     | o           | Masculine ordinal indicator            |
1162 * | U+00C0  | À     | A           | Latin capital letter A with grave      |
1163 * | U+00C1  | Á     | A           | Latin capital letter A with acute      |
1164 * | U+00C2  |      | A           | Latin capital letter A with circumflex |
1165 * | U+00C3  | à    | A           | Latin capital letter A with tilde      |
1166 * | U+00C4  | Ä     | A           | Latin capital letter A with diaeresis  |
1167 * | U+00C5  | Å     | A           | Latin capital letter A with ring above |
1168 * | U+00C6  | Æ     | AE          | Latin capital letter AE                |
1169 * | U+00C7  | Ç     | C           | Latin capital letter C with cedilla    |
1170 * | U+00C8  | È     | E           | Latin capital letter E with grave      |
1171 * | U+00C9  | É     | E           | Latin capital letter E with acute      |
1172 * | U+00CA  | Ê     | E           | Latin capital letter E with circumflex |
1173 * | U+00CB  | Ë     | E           | Latin capital letter E with diaeresis  |
1174 * | U+00CC  | Ì     | I           | Latin capital letter I with grave      |
1175 * | U+00CD  | Í     | I           | Latin capital letter I with acute      |
1176 * | U+00CE  | Π    | I           | Latin capital letter I with circumflex |
1177 * | U+00CF  | Ï     | I           | Latin capital letter I with diaeresis  |
1178 * | U+00D0  | Р    | D           | Latin capital letter Eth               |
1179 * | U+00D1  | Ñ     | N           | Latin capital letter N with tilde      |
1180 * | U+00D2  | Ò     | O           | Latin capital letter O with grave      |
1181 * | U+00D3  | Ó     | O           | Latin capital letter O with acute      |
1182 * | U+00D4  | Ô     | O           | Latin capital letter O with circumflex |
1183 * | U+00D5  | Õ     | O           | Latin capital letter O with tilde      |
1184 * | U+00D6  | Ö     | O           | Latin capital letter O with diaeresis  |
1185 * | U+00D8  | Ø     | O           | Latin capital letter O with stroke     |
1186 * | U+00D9  | Ù     | U           | Latin capital letter U with grave      |
1187 * | U+00DA  | Ú     | U           | Latin capital letter U with acute      |
1188 * | U+00DB  | Û     | U           | Latin capital letter U with circumflex |
1189 * | U+00DC  | Ü     | U           | Latin capital letter U with diaeresis  |
1190 * | U+00DD  | Ý     | Y           | Latin capital letter Y with acute      |
1191 * | U+00DE  | Þ     | TH          | Latin capital letter Thorn             |
1192 * | U+00DF  | ß     | s           | Latin small letter sharp s             |
1193 * | U+00E0  | à     | a           | Latin small letter a with grave        |
1194 * | U+00E1  | á     | a           | Latin small letter a with acute        |
1195 * | U+00E2  | â     | a           | Latin small letter a with circumflex   |
1196 * | U+00E3  | ã     | a           | Latin small letter a with tilde        |
1197 * | U+00E4  | ä     | a           | Latin small letter a with diaeresis    |
1198 * | U+00E5  | å     | a           | Latin small letter a with ring above   |
1199 * | U+00E6  | æ     | ae          | Latin small letter ae                  |
1200 * | U+00E7  | ç     | c           | Latin small letter c with cedilla      |
1201 * | U+00E8  | è     | e           | Latin small letter e with grave        |
1202 * | U+00E9  | é     | e           | Latin small letter e with acute        |
1203 * | U+00EA  | ê     | e           | Latin small letter e with circumflex   |
1204 * | U+00EB  | ë     | e           | Latin small letter e with diaeresis    |
1205 * | U+00EC  | ì     | i           | Latin small letter i with grave        |
1206 * | U+00ED  | í     | i           | Latin small letter i with acute        |
1207 * | U+00EE  | î     | i           | Latin small letter i with circumflex   |
1208 * | U+00EF  | ï     | i           | Latin small letter i with diaeresis    |
1209 * | U+00F0  | ð     | d           | Latin small letter Eth                 |
1210 * | U+00F1  | ñ     | n           | Latin small letter n with tilde        |
1211 * | U+00F2  | ò     | o           | Latin small letter o with grave        |
1212 * | U+00F3  | ó     | o           | Latin small letter o with acute        |
1213 * | U+00F4  | ô     | o           | Latin small letter o with circumflex   |
1214 * | U+00F5  | õ     | o           | Latin small letter o with tilde        |
1215 * | U+00F6  | ö     | o           | Latin small letter o with diaeresis    |
1216 * | U+00F8  | ø     | o           | Latin small letter o with stroke       |
1217 * | U+00F9  | ù     | u           | Latin small letter u with grave        |
1218 * | U+00FA  | ú     | u           | Latin small letter u with acute        |
1219 * | U+00FB  | û     | u           | Latin small letter u with circumflex   |
1220 * | U+00FC  | ü     | u           | Latin small letter u with diaeresis    |
1221 * | U+00FD  | ý     | y           | Latin small letter y with acute        |
1222 * | U+00FE  | þ     | th          | Latin small letter Thorn               |
1223 * | U+00FF  | ÿ     | y           | Latin small letter y with diaeresis    |
1224 *
1225 * Decompositions for Latin Extended-A:
1226 *
1227 * |  Code   | Glyph | Replacement |                    Description                    |
1228 * | ------- | ----- | ----------- | ------------------------------------------------- |
1229 * | U+0100  | Ā     | A           | Latin capital letter A with macron                |
1230 * | U+0101  | ā     | a           | Latin small letter a with macron                  |
1231 * | U+0102  | Ă     | A           | Latin capital letter A with breve                 |
1232 * | U+0103  | ă     | a           | Latin small letter a with breve                   |
1233 * | U+0104  | Ą     | A           | Latin capital letter A with ogonek                |
1234 * | U+0105  | ą     | a           | Latin small letter a with ogonek                  |
1235 * | U+01006 | Ć     | C           | Latin capital letter C with acute                 |
1236 * | U+0107  | ć     | c           | Latin small letter c with acute                   |
1237 * | U+0108  | Ĉ     | C           | Latin capital letter C with circumflex            |
1238 * | U+0109  | ĉ     | c           | Latin small letter c with circumflex              |
1239 * | U+010A  | Ċ     | C           | Latin capital letter C with dot above             |
1240 * | U+010B  | ċ     | c           | Latin small letter c with dot above               |
1241 * | U+010C  | Č     | C           | Latin capital letter C with caron                 |
1242 * | U+010D  | č     | c           | Latin small letter c with caron                   |
1243 * | U+010E  | Ď     | D           | Latin capital letter D with caron                 |
1244 * | U+010F  | ď     | d           | Latin small letter d with caron                   |
1245 * | U+0110  | Đ     | D           | Latin capital letter D with stroke                |
1246 * | U+0111  | đ     | d           | Latin small letter d with stroke                  |
1247 * | U+0112  | Ē     | E           | Latin capital letter E with macron                |
1248 * | U+0113  | ē     | e           | Latin small letter e with macron                  |
1249 * | U+0114  | Ĕ     | E           | Latin capital letter E with breve                 |
1250 * | U+0115  | ĕ     | e           | Latin small letter e with breve                   |
1251 * | U+0116  | Ė     | E           | Latin capital letter E with dot above             |
1252 * | U+0117  | ė     | e           | Latin small letter e with dot above               |
1253 * | U+0118  | Ę     | E           | Latin capital letter E with ogonek                |
1254 * | U+0119  | ę     | e           | Latin small letter e with ogonek                  |
1255 * | U+011A  | Ě     | E           | Latin capital letter E with caron                 |
1256 * | U+011B  | ě     | e           | Latin small letter e with caron                   |
1257 * | U+011C  | Ĝ     | G           | Latin capital letter G with circumflex            |
1258 * | U+011D  | ĝ     | g           | Latin small letter g with circumflex              |
1259 * | U+011E  | Ğ     | G           | Latin capital letter G with breve                 |
1260 * | U+011F  | ğ     | g           | Latin small letter g with breve                   |
1261 * | U+0120  | Ġ     | G           | Latin capital letter G with dot above             |
1262 * | U+0121  | ġ     | g           | Latin small letter g with dot above               |
1263 * | U+0122  | Ģ     | G           | Latin capital letter G with cedilla               |
1264 * | U+0123  | ģ     | g           | Latin small letter g with cedilla                 |
1265 * | U+0124  | Ĥ     | H           | Latin capital letter H with circumflex            |
1266 * | U+0125  | ĥ     | h           | Latin small letter h with circumflex              |
1267 * | U+0126  | Ħ     | H           | Latin capital letter H with stroke                |
1268 * | U+0127  | ħ     | h           | Latin small letter h with stroke                  |
1269 * | U+0128  | Ĩ     | I           | Latin capital letter I with tilde                 |
1270 * | U+0129  | ĩ     | i           | Latin small letter i with tilde                   |
1271 * | U+012A  | Ī     | I           | Latin capital letter I with macron                |
1272 * | U+012B  | ī     | i           | Latin small letter i with macron                  |
1273 * | U+012C  | Ĭ     | I           | Latin capital letter I with breve                 |
1274 * | U+012D  | ĭ     | i           | Latin small letter i with breve                   |
1275 * | U+012E  | Į     | I           | Latin capital letter I with ogonek                |
1276 * | U+012F  | į     | i           | Latin small letter i with ogonek                  |
1277 * | U+0130  | İ     | I           | Latin capital letter I with dot above             |
1278 * | U+0131  | ı     | i           | Latin small letter dotless i                      |
1279 * | U+0132  | IJ     | IJ          | Latin capital ligature IJ                         |
1280 * | U+0133  | ij     | ij          | Latin small ligature ij                           |
1281 * | U+0134  | Ĵ     | J           | Latin capital letter J with circumflex            |
1282 * | U+0135  | ĵ     | j           | Latin small letter j with circumflex              |
1283 * | U+0136  | Ķ     | K           | Latin capital letter K with cedilla               |
1284 * | U+0137  | ķ     | k           | Latin small letter k with cedilla                 |
1285 * | U+0138  | ĸ     | k           | Latin small letter Kra                            |
1286 * | U+0139  | Ĺ     | L           | Latin capital letter L with acute                 |
1287 * | U+013A  | ĺ     | l           | Latin small letter l with acute                   |
1288 * | U+013B  | Ļ     | L           | Latin capital letter L with cedilla               |
1289 * | U+013C  | ļ     | l           | Latin small letter l with cedilla                 |
1290 * | U+013D  | Ľ     | L           | Latin capital letter L with caron                 |
1291 * | U+013E  | ľ     | l           | Latin small letter l with caron                   |
1292 * | U+013F  | Ŀ     | L           | Latin capital letter L with middle dot            |
1293 * | U+0140  | ŀ     | l           | Latin small letter l with middle dot              |
1294 * | U+0141  | Ł     | L           | Latin capital letter L with stroke                |
1295 * | U+0142  | ł     | l           | Latin small letter l with stroke                  |
1296 * | U+0143  | Ń     | N           | Latin capital letter N with acute                 |
1297 * | U+0144  | ń     | n           | Latin small letter N with acute                   |
1298 * | U+0145  | Ņ     | N           | Latin capital letter N with cedilla               |
1299 * | U+0146  | ņ     | n           | Latin small letter n with cedilla                 |
1300 * | U+0147  | Ň     | N           | Latin capital letter N with caron                 |
1301 * | U+0148  | ň     | n           | Latin small letter n with caron                   |
1302 * | U+0149  | ʼn     | n           | Latin small letter n preceded by apostrophe       |
1303 * | U+014A  | Ŋ     | N           | Latin capital letter Eng                          |
1304 * | U+014B  | ŋ     | n           | Latin small letter Eng                            |
1305 * | U+014C  | Ō     | O           | Latin capital letter O with macron                |
1306 * | U+014D  | ō     | o           | Latin small letter o with macron                  |
1307 * | U+014E  | Ŏ     | O           | Latin capital letter O with breve                 |
1308 * | U+014F  | ŏ     | o           | Latin small letter o with breve                   |
1309 * | U+0150  | Ő     | O           | Latin capital letter O with double acute          |
1310 * | U+0151  | ő     | o           | Latin small letter o with double acute            |
1311 * | U+0152  | Œ     | OE          | Latin capital ligature OE                         |
1312 * | U+0153  | œ     | oe          | Latin small ligature oe                           |
1313 * | U+0154  | Ŕ     | R           | Latin capital letter R with acute                 |
1314 * | U+0155  | ŕ     | r           | Latin small letter r with acute                   |
1315 * | U+0156  | Ŗ     | R           | Latin capital letter R with cedilla               |
1316 * | U+0157  | ŗ     | r           | Latin small letter r with cedilla                 |
1317 * | U+0158  | Ř     | R           | Latin capital letter R with caron                 |
1318 * | U+0159  | ř     | r           | Latin small letter r with caron                   |
1319 * | U+015A  | Ś     | S           | Latin capital letter S with acute                 |
1320 * | U+015B  | ś     | s           | Latin small letter s with acute                   |
1321 * | U+015C  | Ŝ     | S           | Latin capital letter S with circumflex            |
1322 * | U+015D  | ŝ     | s           | Latin small letter s with circumflex              |
1323 * | U+015E  | Ş     | S           | Latin capital letter S with cedilla               |
1324 * | U+015F  | ş     | s           | Latin small letter s with cedilla                 |
1325 * | U+0160  | Š     | S           | Latin capital letter S with caron                 |
1326 * | U+0161  | š     | s           | Latin small letter s with caron                   |
1327 * | U+0162  | Ţ     | T           | Latin capital letter T with cedilla               |
1328 * | U+0163  | ţ     | t           | Latin small letter t with cedilla                 |
1329 * | U+0164  | Ť     | T           | Latin capital letter T with caron                 |
1330 * | U+0165  | ť     | t           | Latin small letter t with caron                   |
1331 * | U+0166  | Ŧ     | T           | Latin capital letter T with stroke                |
1332 * | U+0167  | ŧ     | t           | Latin small letter t with stroke                  |
1333 * | U+0168  | Ũ     | U           | Latin capital letter U with tilde                 |
1334 * | U+0169  | ũ     | u           | Latin small letter u with tilde                   |
1335 * | U+016A  | Ū     | U           | Latin capital letter U with macron                |
1336 * | U+016B  | ū     | u           | Latin small letter u with macron                  |
1337 * | U+016C  | Ŭ     | U           | Latin capital letter U with breve                 |
1338 * | U+016D  | ŭ     | u           | Latin small letter u with breve                   |
1339 * | U+016E  | Ů     | U           | Latin capital letter U with ring above            |
1340 * | U+016F  | ů     | u           | Latin small letter u with ring above              |
1341 * | U+0170  | Ű     | U           | Latin capital letter U with double acute          |
1342 * | U+0171  | ű     | u           | Latin small letter u with double acute            |
1343 * | U+0172  | Ų     | U           | Latin capital letter U with ogonek                |
1344 * | U+0173  | ų     | u           | Latin small letter u with ogonek                  |
1345 * | U+0174  | Ŵ     | W           | Latin capital letter W with circumflex            |
1346 * | U+0175  | ŵ     | w           | Latin small letter w with circumflex              |
1347 * | U+0176  | Ŷ     | Y           | Latin capital letter Y with circumflex            |
1348 * | U+0177  | ŷ     | y           | Latin small letter y with circumflex              |
1349 * | U+0178  | Ÿ     | Y           | Latin capital letter Y with diaeresis             |
1350 * | U+0179  | Ź     | Z           | Latin capital letter Z with acute                 |
1351 * | U+017A  | ź     | z           | Latin small letter z with acute                   |
1352 * | U+017B  | Ż     | Z           | Latin capital letter Z with dot above             |
1353 * | U+017C  | ż     | z           | Latin small letter z with dot above               |
1354 * | U+017D  | Ž     | Z           | Latin capital letter Z with caron                 |
1355 * | U+017E  | ž     | z           | Latin small letter z with caron                   |
1356 * | U+017F  | ſ     | s           | Latin small letter long s                         |
1357 * | U+01A0  | Ơ     | O           | Latin capital letter O with horn                  |
1358 * | U+01A1  | ơ     | o           | Latin small letter o with horn                    |
1359 * | U+01AF  | Ư     | U           | Latin capital letter U with horn                  |
1360 * | U+01B0  | ư     | u           | Latin small letter u with horn                    |
1361 * | U+01CD  | Ǎ     | A           | Latin capital letter A with caron                 |
1362 * | U+01CE  | ǎ     | a           | Latin small letter a with caron                   |
1363 * | U+01CF  | Ǐ     | I           | Latin capital letter I with caron                 |
1364 * | U+01D0  | ǐ     | i           | Latin small letter i with caron                   |
1365 * | U+01D1  | Ǒ     | O           | Latin capital letter O with caron                 |
1366 * | U+01D2  | ǒ     | o           | Latin small letter o with caron                   |
1367 * | U+01D3  | Ǔ     | U           | Latin capital letter U with caron                 |
1368 * | U+01D4  | ǔ     | u           | Latin small letter u with caron                   |
1369 * | U+01D5  | Ǖ     | U           | Latin capital letter U with diaeresis and macron  |
1370 * | U+01D6  | ǖ     | u           | Latin small letter u with diaeresis and macron    |
1371 * | U+01D7  | Ǘ     | U           | Latin capital letter U with diaeresis and acute   |
1372 * | U+01D8  | ǘ     | u           | Latin small letter u with diaeresis and acute     |
1373 * | U+01D9  | Ǚ     | U           | Latin capital letter U with diaeresis and caron   |
1374 * | U+01DA  | ǚ     | u           | Latin small letter u with diaeresis and caron     |
1375 * | U+01DB  | Ǜ     | U           | Latin capital letter U with diaeresis and grave   |
1376 * | U+01DC  | ǜ     | u           | Latin small letter u with diaeresis and grave     |
1377 *
1378 * Decompositions for Latin Extended-B:
1379 *
1380 * |   Code   | Glyph | Replacement |                Description                |
1381 * | -------- | ----- | ----------- | ----------------------------------------- |
1382 * | U+0218   | Ș     | S           | Latin capital letter S with comma below   |
1383 * | U+0219   | ș     | s           | Latin small letter s with comma below     |
1384 * | U+021A   | Ț     | T           | Latin capital letter T with comma below   |
1385 * | U+021B   | ț     | t           | Latin small letter t with comma below     |
1386 *
1387 * Vowels with diacritic (Chinese, Hanyu Pinyin):
1388 *
1389 * |   Code   | Glyph | Replacement |                      Description                      |
1390 * | -------- | ----- | ----------- | ----------------------------------------------------- |
1391 * | U+0251   | ɑ     | a           | Latin small letter alpha                              |
1392 * | U+1EA0   | Ạ     | A           | Latin capital letter A with dot below                 |
1393 * | U+1EA1   | ạ     | a           | Latin small letter a with dot below                   |
1394 * | U+1EA2   | Ả     | A           | Latin capital letter A with hook above                |
1395 * | U+1EA3   | ả     | a           | Latin small letter a with hook above                  |
1396 * | U+1EA4   | Ấ     | A           | Latin capital letter A with circumflex and acute      |
1397 * | U+1EA5   | ấ     | a           | Latin small letter a with circumflex and acute        |
1398 * | U+1EA6   | Ầ     | A           | Latin capital letter A with circumflex and grave      |
1399 * | U+1EA7   | ầ     | a           | Latin small letter a with circumflex and grave        |
1400 * | U+1EA8   | Ẩ     | A           | Latin capital letter A with circumflex and hook above |
1401 * | U+1EA9   | ẩ     | a           | Latin small letter a with circumflex and hook above   |
1402 * | U+1EAA   | Ẫ     | A           | Latin capital letter A with circumflex and tilde      |
1403 * | U+1EAB   | ẫ     | a           | Latin small letter a with circumflex and tilde        |
1404 * | U+1EA6   | Ậ     | A           | Latin capital letter A with circumflex and dot below  |
1405 * | U+1EAD   | ậ     | a           | Latin small letter a with circumflex and dot below    |
1406 * | U+1EAE   | Ắ     | A           | Latin capital letter A with breve and acute           |
1407 * | U+1EAF   | ắ     | a           | Latin small letter a with breve and acute             |
1408 * | U+1EB0   | Ằ     | A           | Latin capital letter A with breve and grave           |
1409 * | U+1EB1   | ằ     | a           | Latin small letter a with breve and grave             |
1410 * | U+1EB2   | Ẳ     | A           | Latin capital letter A with breve and hook above      |
1411 * | U+1EB3   | ẳ     | a           | Latin small letter a with breve and hook above        |
1412 * | U+1EB4   | Ẵ     | A           | Latin capital letter A with breve and tilde           |
1413 * | U+1EB5   | ẵ     | a           | Latin small letter a with breve and tilde             |
1414 * | U+1EB6   | Ặ     | A           | Latin capital letter A with breve and dot below       |
1415 * | U+1EB7   | ặ     | a           | Latin small letter a with breve and dot below         |
1416 * | U+1EB8   | Ẹ     | E           | Latin capital letter E with dot below                 |
1417 * | U+1EB9   | ẹ     | e           | Latin small letter e with dot below                   |
1418 * | U+1EBA   | Ẻ     | E           | Latin capital letter E with hook above                |
1419 * | U+1EBB   | ẻ     | e           | Latin small letter e with hook above                  |
1420 * | U+1EBC   | Ẽ     | E           | Latin capital letter E with tilde                     |
1421 * | U+1EBD   | ẽ     | e           | Latin small letter e with tilde                       |
1422 * | U+1EBE   | Ế     | E           | Latin capital letter E with circumflex and acute      |
1423 * | U+1EBF   | ế     | e           | Latin small letter e with circumflex and acute        |
1424 * | U+1EC0   | Ề     | E           | Latin capital letter E with circumflex and grave      |
1425 * | U+1EC1   | ề     | e           | Latin small letter e with circumflex and grave        |
1426 * | U+1EC2   | Ể     | E           | Latin capital letter E with circumflex and hook above |
1427 * | U+1EC3   | ể     | e           | Latin small letter e with circumflex and hook above   |
1428 * | U+1EC4   | Ễ     | E           | Latin capital letter E with circumflex and tilde      |
1429 * | U+1EC5   | ễ     | e           | Latin small letter e with circumflex and tilde        |
1430 * | U+1EC6   | Ệ     | E           | Latin capital letter E with circumflex and dot below  |
1431 * | U+1EC7   | ệ     | e           | Latin small letter e with circumflex and dot below    |
1432 * | U+1EC8   | Ỉ     | I           | Latin capital letter I with hook above                |
1433 * | U+1EC9   | ỉ     | i           | Latin small letter i with hook above                  |
1434 * | U+1ECA   | Ị     | I           | Latin capital letter I with dot below                 |
1435 * | U+1ECB   | ị     | i           | Latin small letter i with dot below                   |
1436 * | U+1ECC   | Ọ     | O           | Latin capital letter O with dot below                 |
1437 * | U+1ECD   | ọ     | o           | Latin small letter o with dot below                   |
1438 * | U+1ECE   | Ỏ     | O           | Latin capital letter O with hook above                |
1439 * | U+1ECF   | ỏ     | o           | Latin small letter o with hook above                  |
1440 * | U+1ED0   | Ố     | O           | Latin capital letter O with circumflex and acute      |
1441 * | U+1ED1   | ố     | o           | Latin small letter o with circumflex and acute        |
1442 * | U+1ED2   | Ồ     | O           | Latin capital letter O with circumflex and grave      |
1443 * | U+1ED3   | ồ     | o           | Latin small letter o with circumflex and grave        |
1444 * | U+1ED4   | Ổ     | O           | Latin capital letter O with circumflex and hook above |
1445 * | U+1ED5   | ổ     | o           | Latin small letter o with circumflex and hook above   |
1446 * | U+1ED6   | Ỗ     | O           | Latin capital letter O with circumflex and tilde      |
1447 * | U+1ED7   | ỗ     | o           | Latin small letter o with circumflex and tilde        |
1448 * | U+1ED8   | Ộ     | O           | Latin capital letter O with circumflex and dot below  |
1449 * | U+1ED9   | ộ     | o           | Latin small letter o with circumflex and dot below    |
1450 * | U+1EDA   | Ớ     | O           | Latin capital letter O with horn and acute            |
1451 * | U+1EDB   | ớ     | o           | Latin small letter o with horn and acute              |
1452 * | U+1EDC   | Ờ     | O           | Latin capital letter O with horn and grave            |
1453 * | U+1EDD   | ờ     | o           | Latin small letter o with horn and grave              |
1454 * | U+1EDE   | Ở     | O           | Latin capital letter O with horn and hook above       |
1455 * | U+1EDF   | ở     | o           | Latin small letter o with horn and hook above         |
1456 * | U+1EE0   | Ỡ     | O           | Latin capital letter O with horn and tilde            |
1457 * | U+1EE1   | ỡ     | o           | Latin small letter o with horn and tilde              |
1458 * | U+1EE2   | Ợ     | O           | Latin capital letter O with horn and dot below        |
1459 * | U+1EE3   | ợ     | o           | Latin small letter o with horn and dot below          |
1460 * | U+1EE4   | Ụ     | U           | Latin capital letter U with dot below                 |
1461 * | U+1EE5   | ụ     | u           | Latin small letter u with dot below                   |
1462 * | U+1EE6   | Ủ     | U           | Latin capital letter U with hook above                |
1463 * | U+1EE7   | ủ     | u           | Latin small letter u with hook above                  |
1464 * | U+1EE8   | Ứ     | U           | Latin capital letter U with horn and acute            |
1465 * | U+1EE9   | ứ     | u           | Latin small letter u with horn and acute              |
1466 * | U+1EEA   | Ừ     | U           | Latin capital letter U with horn and grave            |
1467 * | U+1EEB   | ừ     | u           | Latin small letter u with horn and grave              |
1468 * | U+1EEC   | Ử     | U           | Latin capital letter U with horn and hook above       |
1469 * | U+1EED   | ử     | u           | Latin small letter u with horn and hook above         |
1470 * | U+1EEE   | Ữ     | U           | Latin capital letter U with horn and tilde            |
1471 * | U+1EEF   | ữ     | u           | Latin small letter u with horn and tilde              |
1472 * | U+1EF0   | Ự     | U           | Latin capital letter U with horn and dot below        |
1473 * | U+1EF1   | ự     | u           | Latin small letter u with horn and dot below          |
1474 * | U+1EF2   | Ỳ     | Y           | Latin capital letter Y with grave                     |
1475 * | U+1EF3   | ỳ     | y           | Latin small letter y with grave                       |
1476 * | U+1EF4   | Ỵ     | Y           | Latin capital letter Y with dot below                 |
1477 * | U+1EF5   | ỵ     | y           | Latin small letter y with dot below                   |
1478 * | U+1EF6   | Ỷ     | Y           | Latin capital letter Y with hook above                |
1479 * | U+1EF7   | ỷ     | y           | Latin small letter y with hook above                  |
1480 * | U+1EF8   | Ỹ     | Y           | Latin capital letter Y with tilde                     |
1481 * | U+1EF9   | ỹ     | y           | Latin small letter y with tilde                       |
1482 *
1483 * German (`de_DE`), German formal (`de_DE_formal`), German (Switzerland) formal (`de_CH`),
1484 * and German (Switzerland) informal (`de_CH_informal`) locales:
1485 *
1486 * |   Code   | Glyph | Replacement |               Description               |
1487 * | -------- | ----- | ----------- | --------------------------------------- |
1488 * | U+00C4   | Ä     | Ae          | Latin capital letter A with diaeresis   |
1489 * | U+00E4   | ä     | ae          | Latin small letter a with diaeresis     |
1490 * | U+00D6   | Ö     | Oe          | Latin capital letter O with diaeresis   |
1491 * | U+00F6   | ö     | oe          | Latin small letter o with diaeresis     |
1492 * | U+00DC   | Ü     | Ue          | Latin capital letter U with diaeresis   |
1493 * | U+00FC   | ü     | ue          | Latin small letter u with diaeresis     |
1494 * | U+00DF   | ß     | ss          | Latin small letter sharp s              |
1495 *
1496 * Danish (`da_DK`) locale:
1497 *
1498 * |   Code   | Glyph | Replacement |               Description               |
1499 * | -------- | ----- | ----------- | --------------------------------------- |
1500 * | U+00C6   | Æ     | Ae          | Latin capital letter AE                 |
1501 * | U+00E6   | æ     | ae          | Latin small letter ae                   |
1502 * | U+00D8   | Ø     | Oe          | Latin capital letter O with stroke      |
1503 * | U+00F8   | ø     | oe          | Latin small letter o with stroke        |
1504 * | U+00C5   | Å     | Aa          | Latin capital letter A with ring above  |
1505 * | U+00E5   | å     | aa          | Latin small letter a with ring above    |
1506 *
1507 * Catalan (`ca`) locale:
1508 *
1509 * |   Code   | Glyph | Replacement |               Description               |
1510 * | -------- | ----- | ----------- | --------------------------------------- |
1511 * | U+00B7   | l·l   | ll          | Flown dot (between two Ls)              |
1512 *
1513 * Serbian (`sr_RS`) and Bosnian (`bs_BA`) locales:
1514 *
1515 * |   Code   | Glyph | Replacement |               Description               |
1516 * | -------- | ----- | ----------- | --------------------------------------- |
1517 * | U+0110   | Đ     | DJ          | Latin capital letter D with stroke      |
1518 * | U+0111   | đ     | dj          | Latin small letter d with stroke        |
1519 *
1520 * @since 1.2.1
1521 * @since 4.6.0 Added locale support for `de_CH`, `de_CH_informal`, and `ca`.
1522 * @since 4.7.0 Added locale support for `sr_RS`.
1523 * @since 4.8.0 Added locale support for `bs_BA`.
1524 *
1525 * @param string $string Text that might have accent characters
1526 * @return string Filtered string with replaced "nice" characters.
1527 */
1528function remove_accents( $string ) {
1529        if ( !preg_match('/[\x80-\xff]/', $string) )
1530                return $string;
1531
1532        if (seems_utf8($string)) {
1533                $chars = array(
1534                // Decompositions for Latin-1 Supplement
1535                'ª' => 'a', 'º' => 'o',
1536                'À' => 'A', 'Á' => 'A',
1537                'Â' => 'A', 'Ã' => 'A',
1538                'Ä' => 'A', 'Å' => 'A',
1539                'Æ' => 'AE','Ç' => 'C',
1540                'È' => 'E', 'É' => 'E',
1541                'Ê' => 'E', 'Ë' => 'E',
1542                'Ì' => 'I', 'Í' => 'I',
1543                'Î' => 'I', 'Ï' => 'I',
1544                'Ð' => 'D', 'Ñ' => 'N',
1545                'Ò' => 'O', 'Ó' => 'O',
1546                'Ô' => 'O', 'Õ' => 'O',
1547                'Ö' => 'O', 'Ù' => 'U',
1548                'Ú' => 'U', 'Û' => 'U',
1549                'Ü' => 'U', 'Ý' => 'Y',
1550                'Þ' => 'TH','ß' => 's',
1551                'à' => 'a', 'á' => 'a',
1552                'â' => 'a', 'ã' => 'a',
1553                'ä' => 'a', 'å' => 'a',
1554                'æ' => 'ae','ç' => 'c',
1555                'è' => 'e', 'é' => 'e',
1556                'ê' => 'e', 'ë' => 'e',
1557                'ì' => 'i', 'í' => 'i',
1558                'î' => 'i', 'ï' => 'i',
1559                'ð' => 'd', 'ñ' => 'n',
1560                'ò' => 'o', 'ó' => 'o',
1561                'ô' => 'o', 'õ' => 'o',
1562                'ö' => 'o', 'ø' => 'o',
1563                'ù' => 'u', 'ú' => 'u',
1564                'û' => 'u', 'ü' => 'u',
1565                'ý' => 'y', 'þ' => 'th',
1566                'ÿ' => 'y', 'Ø' => 'O',
1567                // Decompositions for Latin Extended-A
1568                'Ā' => 'A', 'ā' => 'a',
1569                'Ă' => 'A', 'ă' => 'a',
1570                'Ą' => 'A', 'ą' => 'a',
1571                'Ć' => 'C', 'ć' => 'c',
1572                'Ĉ' => 'C', 'ĉ' => 'c',
1573                'Ċ' => 'C', 'ċ' => 'c',
1574                'Č' => 'C', 'č' => 'c',
1575                'Ď' => 'D', 'ď' => 'd',
1576                'Đ' => 'D', 'đ' => 'd',
1577                'Ē' => 'E', 'ē' => 'e',
1578                'Ĕ' => 'E', 'ĕ' => 'e',
1579                'Ė' => 'E', 'ė' => 'e',
1580                'Ę' => 'E', 'ę' => 'e',
1581                'Ě' => 'E', 'ě' => 'e',
1582                'Ĝ' => 'G', 'ĝ' => 'g',
1583                'Ğ' => 'G', 'ğ' => 'g',
1584                'Ġ' => 'G', 'ġ' => 'g',
1585                'Ģ' => 'G', 'ģ' => 'g',
1586                'Ĥ' => 'H', 'ĥ' => 'h',
1587                'Ħ' => 'H', 'ħ' => 'h',
1588                'Ĩ' => 'I', 'ĩ' => 'i',
1589                'Ī' => 'I', 'ī' => 'i',
1590                'Ĭ' => 'I', 'ĭ' => 'i',
1591                'Į' => 'I', 'į' => 'i',
1592                'İ' => 'I', 'ı' => 'i',
1593                'IJ' => 'IJ','ij' => 'ij',
1594                'Ĵ' => 'J', 'ĵ' => 'j',
1595                'Ķ' => 'K', 'ķ' => 'k',
1596                'ĸ' => 'k', 'Ĺ' => 'L',
1597                'ĺ' => 'l', 'Ļ' => 'L',
1598                'ļ' => 'l', 'Ľ' => 'L',
1599                'ľ' => 'l', 'Ŀ' => 'L',
1600                'ŀ' => 'l', 'Ł' => 'L',
1601                'ł' => 'l', 'Ń' => 'N',
1602                'ń' => 'n', 'Ņ' => 'N',
1603                'ņ' => 'n', 'Ň' => 'N',
1604                'ň' => 'n', 'ʼn' => 'n',
1605                'Ŋ' => 'N', 'ŋ' => 'n',
1606                'Ō' => 'O', 'ō' => 'o',
1607                'Ŏ' => 'O', 'ŏ' => 'o',
1608                'Ő' => 'O', 'ő' => 'o',
1609                'Œ' => 'OE','œ' => 'oe',
1610                'Ŕ' => 'R','ŕ' => 'r',
1611                'Ŗ' => 'R','ŗ' => 'r',
1612                'Ř' => 'R','ř' => 'r',
1613                'Ś' => 'S','ś' => 's',
1614                'Ŝ' => 'S','ŝ' => 's',
1615                'Ş' => 'S','ş' => 's',
1616                'Š' => 'S', 'š' => 's',
1617                'Ţ' => 'T', 'ţ' => 't',
1618                'Ť' => 'T', 'ť' => 't',
1619                'Ŧ' => 'T', 'ŧ' => 't',
1620                'Ũ' => 'U', 'ũ' => 'u',
1621                'Ū' => 'U', 'ū' => 'u',
1622                'Ŭ' => 'U', 'ŭ' => 'u',
1623                'Ů' => 'U', 'ů' => 'u',
1624                'Ű' => 'U', 'ű' => 'u',
1625                'Ų' => 'U', 'ų' => 'u',
1626                'Ŵ' => 'W', 'ŵ' => 'w',
1627                'Ŷ' => 'Y', 'ŷ' => 'y',
1628                'Ÿ' => 'Y', 'Ź' => 'Z',
1629                'ź' => 'z', 'Ż' => 'Z',
1630                'ż' => 'z', 'Ž' => 'Z',
1631                'ž' => 'z', 'ſ' => 's',
1632                // Decompositions for Latin Extended-B
1633                'Ș' => 'S', 'ș' => 's',
1634                'Ț' => 'T', 'ț' => 't',
1635                // Euro Sign
1636                '€' => 'E',
1637                // GBP (Pound) Sign
1638                '£' => '',
1639                // Vowels with diacritic (Vietnamese)
1640                // unmarked
1641                'Ơ' => 'O', 'ơ' => 'o',
1642                'Ư' => 'U', 'ư' => 'u',
1643                // grave accent
1644                'Ầ' => 'A', 'ầ' => 'a',
1645                'Ằ' => 'A', 'ằ' => 'a',
1646                'Ề' => 'E', 'ề' => 'e',
1647                'Ồ' => 'O', 'ồ' => 'o',
1648                'Ờ' => 'O', 'ờ' => 'o',
1649                'Ừ' => 'U', 'ừ' => 'u',
1650                'Ỳ' => 'Y', 'ỳ' => 'y',
1651                // hook
1652                'Ả' => 'A', 'ả' => 'a',
1653                'Ẩ' => 'A', 'ẩ' => 'a',
1654                'Ẳ' => 'A', 'ẳ' => 'a',
1655                'Ẻ' => 'E', 'ẻ' => 'e',
1656                'Ể' => 'E', 'ể' => 'e',
1657                'Ỉ' => 'I', 'ỉ' => 'i',
1658                'Ỏ' => 'O', 'ỏ' => 'o',
1659                'Ổ' => 'O', 'ổ' => 'o',
1660                'Ở' => 'O', 'ở' => 'o',
1661                'Ủ' => 'U', 'ủ' => 'u',
1662                'Ử' => 'U', 'ử' => 'u',
1663                'Ỷ' => 'Y', 'ỷ' => 'y',
1664                // tilde
1665                'Ẫ' => 'A', 'ẫ' => 'a',
1666                'Ẵ' => 'A', 'ẵ' => 'a',
1667                'Ẽ' => 'E', 'ẽ' => 'e',
1668                'Ễ' => 'E', 'ễ' => 'e',
1669                'Ỗ' => 'O', 'ỗ' => 'o',
1670                'Ỡ' => 'O', 'ỡ' => 'o',
1671                'Ữ' => 'U', 'ữ' => 'u',
1672                'Ỹ' => 'Y', 'ỹ' => 'y',
1673                // acute accent
1674                'Ấ' => 'A', 'ấ' => 'a',
1675                'Ắ' => 'A', 'ắ' => 'a',
1676                'Ế' => 'E', 'ế' => 'e',
1677                'Ố' => 'O', 'ố' => 'o',
1678                'Ớ' => 'O', 'ớ' => 'o',
1679                'Ứ' => 'U', 'ứ' => 'u',
1680                // dot below
1681                'Ạ' => 'A', 'ạ' => 'a',
1682                'Ậ' => 'A', 'ậ' => 'a',
1683                'Ặ' => 'A', 'ặ' => 'a',
1684                'Ẹ' => 'E', 'ẹ' => 'e',
1685                'Ệ' => 'E', 'ệ' => 'e',
1686                'Ị' => 'I', 'ị' => 'i',
1687                'Ọ' => 'O', 'ọ' => 'o',
1688                'Ộ' => 'O', 'ộ' => 'o',
1689                'Ợ' => 'O', 'ợ' => 'o',
1690                'Ụ' => 'U', 'ụ' => 'u',
1691                'Ự' => 'U', 'ự' => 'u',
1692                'Ỵ' => 'Y', 'ỵ' => 'y',
1693                // Vowels with diacritic (Chinese, Hanyu Pinyin)
1694                'ɑ' => 'a',
1695                // macron
1696                'Ǖ' => 'U', 'ǖ' => 'u',
1697                // acute accent
1698                'Ǘ' => 'U', 'ǘ' => 'u',
1699                // caron
1700                'Ǎ' => 'A', 'ǎ' => 'a',
1701                'Ǐ' => 'I', 'ǐ' => 'i',
1702                'Ǒ' => 'O', 'ǒ' => 'o',
1703                'Ǔ' => 'U', 'ǔ' => 'u',
1704                'Ǚ' => 'U', 'ǚ' => 'u',
1705                // grave accent
1706                'Ǜ' => 'U', 'ǜ' => 'u',
1707                );
1708
1709                // Used for locale-specific rules
1710                $locale = get_locale();
1711
1712                if ( 'de_DE' == $locale || 'de_DE_formal' == $locale || 'de_CH' == $locale || 'de_CH_informal' == $locale ) {
1713                        $chars[ 'Ä' ] = 'Ae';
1714                        $chars[ 'ä' ] = 'ae';
1715                        $chars[ 'Ö' ] = 'Oe';
1716                        $chars[ 'ö' ] = 'oe';
1717                        $chars[ 'Ü' ] = 'Ue';
1718                        $chars[ 'ü' ] = 'ue';
1719                        $chars[ 'ß' ] = 'ss';
1720                } elseif ( 'da_DK' === $locale ) {
1721                        $chars[ 'Æ' ] = 'Ae';
1722                        $chars[ 'æ' ] = 'ae';
1723                        $chars[ 'Ø' ] = 'Oe';
1724                        $chars[ 'ø' ] = 'oe';
1725                        $chars[ 'Å' ] = 'Aa';
1726                        $chars[ 'å' ] = 'aa';
1727                } elseif ( 'ca' === $locale ) {
1728                        $chars[ 'l·l' ] = 'll';
1729                } elseif ( 'sr_RS' === $locale || 'bs_BA' === $locale ) {
1730                        $chars[ 'Đ' ] = 'DJ';
1731                        $chars[ 'đ' ] = 'dj';
1732                }
1733
1734                $string = strtr($string, $chars);
1735        } else {
1736                $chars = array();
1737                // Assume ISO-8859-1 if not UTF-8
1738                $chars['in'] = "\x80\x83\x8a\x8e\x9a\x9e"
1739                        ."\x9f\xa2\xa5\xb5\xc0\xc1\xc2"
1740                        ."\xc3\xc4\xc5\xc7\xc8\xc9\xca"
1741                        ."\xcb\xcc\xcd\xce\xcf\xd1\xd2"
1742                        ."\xd3\xd4\xd5\xd6\xd8\xd9\xda"
1743                        ."\xdb\xdc\xdd\xe0\xe1\xe2\xe3"
1744                        ."\xe4\xe5\xe7\xe8\xe9\xea\xeb"
1745                        ."\xec\xed\xee\xef\xf1\xf2\xf3"
1746                        ."\xf4\xf5\xf6\xf8\xf9\xfa\xfb"
1747                        ."\xfc\xfd\xff";
1748
1749                $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
1750
1751                $string = strtr($string, $chars['in'], $chars['out']);
1752                $double_chars = array();
1753                $double_chars['in'] = array("\x8c", "\x9c", "\xc6", "\xd0", "\xde", "\xdf", "\xe6", "\xf0", "\xfe");
1754                $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
1755                $string = str_replace($double_chars['in'], $double_chars['out'], $string);
1756        }
1757
1758        return $string;
1759}
1760
1761/**
1762 * Sanitizes a filename, replacing whitespace with dashes.
1763 *
1764 * Removes special characters that are illegal in filenames on certain
1765 * operating systems and special characters requiring special escaping
1766 * to manipulate at the command line. Replaces spaces and consecutive
1767 * dashes with a single dash. Trims period, dash and underscore from beginning
1768 * and end of filename. It is not guaranteed that this function will return a
1769 * filename that is allowed to be uploaded.
1770 *
1771 * @since 2.1.0
1772 *
1773 * @param string $filename The filename to be sanitized
1774 * @return string The sanitized filename
1775 */
1776function sanitize_file_name( $filename ) {
1777        $filename_raw = $filename;
1778        $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
1779        /**
1780         * Filters the list of characters to remove from a filename.
1781         *
1782         * @since 2.8.0
1783         *
1784         * @param array  $special_chars Characters to remove.
1785         * @param string $filename_raw  Filename as it was passed into sanitize_file_name().
1786         */
1787        $special_chars = apply_filters( 'sanitize_file_name_chars', $special_chars, $filename_raw );
1788        $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename );
1789        $filename = str_replace( $special_chars, '', $filename );
1790        $filename = str_replace( array( '%20', '+' ), '-', $filename );
1791        $filename = preg_replace( '/[\r\n\t -]+/', '-', $filename );
1792        $filename = trim( $filename, '.-_' );
1793
1794        if ( false === strpos( $filename, '.' ) ) {
1795                $mime_types = wp_get_mime_types();
1796                $filetype = wp_check_filetype( 'test.' . $filename, $mime_types );
1797                if ( $filetype['ext'] === $filename ) {
1798                        $filename = 'unnamed-file.' . $filetype['ext'];
1799                }
1800        }
1801
1802        // Split the filename into a base and extension[s]
1803        $parts = explode('.', $filename);
1804
1805        // Return if only one extension
1806        if ( count( $parts ) <= 2 ) {
1807                /**
1808                 * Filters a sanitized filename string.
1809                 *
1810                 * @since 2.8.0
1811                 *
1812                 * @param string $filename     Sanitized filename.
1813                 * @param string $filename_raw The filename prior to sanitization.
1814                 */
1815                return apply_filters( 'sanitize_file_name', $filename, $filename_raw );
1816        }
1817
1818        // Process multiple extensions
1819        $filename = array_shift($parts);
1820        $extension = array_pop($parts);
1821        $mimes = get_allowed_mime_types();
1822
1823        /*
1824         * Loop over any intermediate extensions. Postfix them with a trailing underscore
1825         * if they are a 2 - 5 character long alpha string not in the extension whitelist.
1826         */
1827        foreach ( (array) $parts as $part) {
1828                $filename .= '.' . $part;
1829
1830                if ( preg_match("/^[a-zA-Z]{2,5}\d?$/", $part) ) {
1831                        $allowed = false;
1832                        foreach ( $mimes as $ext_preg => $mime_match ) {
1833                                $ext_preg = '!^(' . $ext_preg . ')$!i';
1834                                if ( preg_match( $ext_preg, $part ) ) {
1835                                        $allowed = true;
1836                                        break;
1837                                }
1838                        }
1839                        if ( !$allowed )
1840                                $filename .= '_';
1841                }
1842        }
1843        $filename .= '.' . $extension;
1844        /** This filter is documented in wp-includes/formatting.php */
1845        return apply_filters('sanitize_file_name', $filename, $filename_raw);
1846}
1847
1848/**
1849 * Sanitizes a username, stripping out unsafe characters.
1850 *
1851 * Removes tags, octets, entities, and if strict is enabled, will only keep
1852 * alphanumeric, _, space, ., -, @. After sanitizing, it passes the username,
1853 * raw username (the username in the parameter), and the value of $strict as
1854 * parameters for the {@see 'sanitize_user'} filter.
1855 *
1856 * @since 2.0.0
1857 *
1858 * @param string $username The username to be sanitized.
1859 * @param bool   $strict   If set limits $username to specific characters. Default false.
1860 * @return string The sanitized username, after passing through filters.
1861 */
1862function sanitize_user( $username, $strict = false ) {
1863        $raw_username = $username;
1864        $username = wp_strip_all_tags( $username );
1865        $username = remove_accents( $username );
1866        // Kill octets
1867        $username = preg_replace( '|%([a-fA-F0-9][a-fA-F0-9])|', '', $username );
1868        $username = preg_replace( '/&.+?;/', '', $username ); // Kill entities
1869
1870        // If strict, reduce to ASCII for max portability.
1871        if ( $strict )
1872                $username = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $username );
1873
1874        $username = trim( $username );
1875        // Consolidate contiguous whitespace
1876        $username = preg_replace( '|\s+|', ' ', $username );
1877
1878        /**
1879         * Filters a sanitized username string.
1880         *
1881         * @since 2.0.1
1882         *
1883         * @param string $username     Sanitized username.
1884         * @param string $raw_username The username prior to sanitization.
1885         * @param bool   $strict       Whether to limit the sanitization to specific characters. Default false.
1886         */
1887        return apply_filters( 'sanitize_user', $username, $raw_username, $strict );
1888}
1889
1890/**
1891 * Sanitizes a string key.
1892 *
1893 * Keys are used as internal identifiers. Lowercase alphanumeric characters, dashes and underscores are allowed.
1894 *
1895 * @since 3.0.0
1896 *
1897 * @param string $key String key
1898 * @return string Sanitized key
1899 */
1900function sanitize_key( $key ) {
1901        $raw_key = $key;
1902        $key = strtolower( $key );
1903        $key = preg_replace( '/[^a-z0-9_\-]/', '', $key );
1904
1905        /**
1906         * Filters a sanitized key string.
1907         *
1908         * @since 3.0.0
1909         *
1910         * @param string $key     Sanitized key.
1911         * @param string $raw_key The key prior to sanitization.
1912         */
1913        return apply_filters( 'sanitize_key', $key, $raw_key );
1914}
1915
1916/**
1917 * Sanitizes a title, or returns a fallback title.
1918 *
1919 * Specifically, HTML and PHP tags are stripped. Further actions can be added
1920 * via the plugin API. If $title is empty and $fallback_title is set, the latter
1921 * will be used.
1922 *
1923 * @since 1.0.0
1924 *
1925 * @param string $title          The string to be sanitized.
1926 * @param string $fallback_title Optional. A title to use if $title is empty.
1927 * @param string $context        Optional. The operation for which the string is sanitized
1928 * @return string The sanitized string.
1929 */
1930function sanitize_title( $title, $fallback_title = '', $context = 'save' ) {
1931        $raw_title = $title;
1932
1933        if ( 'save' == $context )
1934                $title = remove_accents($title);
1935
1936        /**
1937         * Filters a sanitized title string.
1938         *
1939         * @since 1.2.0
1940         *
1941         * @param string $title     Sanitized title.
1942         * @param string $raw_title The title prior to sanitization.
1943         * @param string $context   The context for which the title is being sanitized.
1944         */
1945        $title = apply_filters( 'sanitize_title', $title, $raw_title, $context );
1946
1947        if ( '' === $title || false === $title )
1948                $title = $fallback_title;
1949
1950        return $title;
1951}
1952
1953/**
1954 * Sanitizes a title with the 'query' context.
1955 *
1956 * Used for querying the database for a value from URL.
1957 *
1958 * @since 3.1.0
1959 *
1960 * @param string $title The string to be sanitized.
1961 * @return string The sanitized string.
1962 */
1963function sanitize_title_for_query( $title ) {
1964        return sanitize_title( $title, '', 'query' );
1965}
1966
1967/**
1968 * Sanitizes a title, replacing whitespace and a few other characters with dashes.
1969 *
1970 * Limits the output to alphanumeric characters, underscore (_) and dash (-).
1971 * Whitespace becomes a dash.
1972 *
1973 * @since 1.2.0
1974 *
1975 * @param string $title     The title to be sanitized.
1976 * @param string $raw_title Optional. Not used.
1977 * @param string $context   Optional. The operation for which the string is sanitized.
1978 * @return string The sanitized title.
1979 */
1980function sanitize_title_with_dashes( $title, $raw_title = '', $context = 'display' ) {
1981        $title = strip_tags($title);
1982        // Preserve escaped octets.
1983        $title = preg_replace('|%([a-fA-F0-9][a-fA-F0-9])|', '---$1---', $title);
1984        // Remove percent signs that are not part of an octet.
1985        $title = str_replace('%', '', $title);
1986        // Restore octets.
1987        $title = preg_replace('|---([a-fA-F0-9][a-fA-F0-9])---|', '%$1', $title);
1988
1989        if (seems_utf8($title)) {
1990                if (function_exists('mb_strtolower')) {
1991                        $title = mb_strtolower($title, 'UTF-8');
1992                }
1993                $title = utf8_uri_encode($title, 200);
1994        }
1995
1996        $title = strtolower($title);
1997
1998        if ( 'save' == $context ) {
1999                // Convert nbsp, ndash and mdash to hyphens
2000                $title = str_replace( array( '%c2%a0', '%e2%80%93', '%e2%80%94' ), '-', $title );
2001                // Convert nbsp, ndash and mdash HTML entities to hyphens
2002                $title = str_replace( array( '&nbsp;', '&#160;', '&ndash;', '&#8211;', '&mdash;', '&#8212;' ), '-', $title );
2003
2004                // Strip these characters entirely
2005                $title = str_replace( array(
2006                        // iexcl and iquest
2007                        '%c2%a1', '%c2%bf',
2008                        // angle quotes
2009                        '%c2%ab', '%c2%bb', '%e2%80%b9', '%e2%80%ba',
2010                        // curly quotes
2011                        '%e2%80%98', '%e2%80%99', '%e2%80%9c', '%e2%80%9d',
2012                        '%e2%80%9a', '%e2%80%9b', '%e2%80%9e', '%e2%80%9f',
2013                        // copy, reg, deg, hellip and trade
2014                        '%c2%a9', '%c2%ae', '%c2%b0', '%e2%80%a6', '%e2%84%a2',
2015                        // acute accents
2016                        '%c2%b4', '%cb%8a', '%cc%81', '%cd%81',
2017                        // grave accent, macron, caron
2018                        '%cc%80', '%cc%84', '%cc%8c',
2019                ), '', $title );
2020
2021                // Convert times to x
2022                $title = str_replace( '%c3%97', 'x', $title );
2023        }
2024
2025        $title = preg_replace('/&.+?;/', '', $title); // kill entities
2026        $title = str_replace('.', '-', $title);
2027
2028        $title = preg_replace('/[^%a-z0-9 _-]/', '', $title);
2029        $title = preg_replace('/\s+/', '-', $title);
2030        $title = preg_replace('|-+|', '-', $title);
2031        $title = trim($title, '-');
2032
2033        return $title;
2034}
2035
2036/**
2037 * Ensures a string is a valid SQL 'order by' clause.
2038 *
2039 * Accepts one or more columns, with or without a sort order (ASC / DESC).
2040 * e.g. 'column_1', 'column_1, column_2', 'column_1 ASC, column_2 DESC' etc.
2041 *
2042 * Also accepts 'RAND()'.
2043 *
2044 * @since 2.5.1
2045 *
2046 * @param string $orderby Order by clause to be validated.
2047 * @return string|false Returns $orderby if valid, false otherwise.
2048 */
2049function sanitize_sql_orderby( $orderby ) {
2050        if ( preg_match( '/^\s*(([a-z0-9_]+|`[a-z0-9_]+`)(\s+(ASC|DESC))?\s*(,\s*(?=[a-z0-9_`])|$))+$/i', $orderby ) || preg_match( '/^\s*RAND\(\s*\)\s*$/i', $orderby ) ) {
2051                return $orderby;
2052        }
2053        return false;
2054}
2055
2056/**
2057 * Sanitizes an HTML classname to ensure it only contains valid characters.
2058 *
2059 * Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty
2060 * string then it will return the alternative value supplied.
2061 *
2062 * @todo Expand to support the full range of CDATA that a class attribute can contain.
2063 *
2064 * @since 2.8.0
2065 *
2066 * @param string $class    The classname to be sanitized
2067 * @param string $fallback Optional. The value to return if the sanitization ends up as an empty string.
2068 *      Defaults to an empty string.
2069 * @return string The sanitized value
2070 */
2071function sanitize_html_class( $class, $fallback = '' ) {
2072        //Strip out any % encoded octets
2073        $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $class );
2074
2075        //Limit to A-Z,a-z,0-9,_,-
2076        $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized );
2077
2078        if ( '' == $sanitized && $fallback ) {
2079                return sanitize_html_class( $fallback );
2080        }
2081        /**
2082         * Filters a sanitized HTML class string.
2083         *
2084         * @since 2.8.0
2085         *
2086         * @param string $sanitized The sanitized HTML class.
2087         * @param string $class     HTML class before sanitization.
2088         * @param string $fallback  The fallback string.
2089         */
2090        return apply_filters( 'sanitize_html_class', $sanitized, $class, $fallback );
2091}
2092
2093/**
2094 * Converts lone & characters into `&#038;` (a.k.a. `&amp;`)
2095 *
2096 * @since 0.71
2097 *
2098 * @param string $content    String of characters to be converted.
2099 * @param string $deprecated Not used.
2100 * @return string Converted string.
2101 */
2102function convert_chars( $content, $deprecated = '' ) {
2103        if ( ! empty( $deprecated ) ) {
2104                _deprecated_argument( __FUNCTION__, '0.71' );
2105        }
2106
2107        if ( strpos( $content, '&' ) !== false ) {
2108                $content = preg_replace( '/&([^#])(?![a-z1-4]{1,8};)/i', '&#038;$1', $content );
2109        }
2110
2111        return $content;
2112}
2113
2114/**
2115 * Converts invalid Unicode references range to valid range.
2116 *
2117 * @since 4.3.0
2118 *
2119 * @param string $content String with entities that need converting.
2120 * @return string Converted string.
2121 */
2122function convert_invalid_entities( $content ) {
2123        $wp_htmltranswinuni = array(
2124                '&#128;' => '&#8364;', // the Euro sign
2125                '&#129;' => '',
2126                '&#130;' => '&#8218;', // these are Windows CP1252 specific characters
2127                '&#131;' => '&#402;',  // they would look weird on non-Windows browsers
2128                '&#132;' => '&#8222;',
2129                '&#133;' => '&#8230;',
2130                '&#134;' => '&#8224;',
2131                '&#135;' => '&#8225;',
2132                '&#136;' => '&#710;',
2133                '&#137;' => '&#8240;',
2134                '&#138;' => '&#352;',
2135                '&#139;' => '&#8249;',
2136                '&#140;' => '&#338;',
2137                '&#141;' => '',
2138                '&#142;' => '&#381;',
2139                '&#143;' => '',
2140                '&#144;' => '',
2141                '&#145;' => '&#8216;',
2142                '&#146;' => '&#8217;',
2143                '&#147;' => '&#8220;',
2144                '&#148;' => '&#8221;',
2145                '&#149;' => '&#8226;',
2146                '&#150;' => '&#8211;',
2147                '&#151;' => '&#8212;',
2148                '&#152;' => '&#732;',
2149                '&#153;' => '&#8482;',
2150                '&#154;' => '&#353;',
2151                '&#155;' => '&#8250;',
2152                '&#156;' => '&#339;',
2153                '&#157;' => '',
2154                '&#158;' => '&#382;',
2155                '&#159;' => '&#376;'
2156        );
2157
2158        if ( strpos( $content, '&#1' ) !== false ) {
2159                $content = strtr( $content, $wp_htmltranswinuni );
2160        }
2161
2162        return $content;
2163}
2164
2165/**
2166 * Balances tags if forced to, or if the 'use_balanceTags' option is set to true.
2167 *
2168 * @since 0.71
2169 *
2170 * @param string $text  Text to be balanced
2171 * @param bool   $force If true, forces balancing, ignoring the value of the option. Default false.
2172 * @return string Balanced text
2173 */
2174function balanceTags( $text, $force = false ) {
2175        if ( $force || get_option('use_balanceTags') == 1 ) {
2176                return force_balance_tags( $text );
2177        } else {
2178                return $text;
2179        }
2180}
2181
2182/**
2183 * Balances tags of string using a modified stack.
2184 *
2185 * @since 2.0.4
2186 *
2187 * @author Leonard Lin <leonard@acm.org>
2188 * @license GPL
2189 * @copyright November 4, 2001
2190 * @version 1.1
2191 * @todo Make better - change loop condition to $text in 1.2
2192 * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004
2193 *              1.1  Fixed handling of append/stack pop order of end text
2194 *                       Added Cleaning Hooks
2195 *              1.0  First Version
2196 *
2197 * @param string $text Text to be balanced.
2198 * @return string Balanced text.
2199 */
2200function force_balance_tags( $text ) {
2201        $tagstack = array();
2202        $stacksize = 0;
2203        $tagqueue = '';
2204        $newtext = '';
2205        // Known single-entity/self-closing tags
2206        $single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
2207        // Tags that can be immediately nested within themselves
2208        $nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
2209
2210        // WP bug fix for comments - in case you REALLY meant to type '< !--'
2211        $text = str_replace('< !--', '<    !--', $text);
2212        // WP bug fix for LOVE <3 (and other situations with '<' before a number)
2213        $text = preg_replace('#<([0-9]{1})#', '&lt;$1', $text);
2214
2215        while ( preg_match("/<(\/?[\w:]*)\s*([^>]*)>/", $text, $regex) ) {
2216                $newtext .= $tagqueue;
2217
2218                $i = strpos($text, $regex[0]);
2219                $l = strlen($regex[0]);
2220
2221                // clear the shifter
2222                $tagqueue = '';
2223                // Pop or Push
2224                if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag
2225                        $tag = strtolower(substr($regex[1],1));
2226                        // if too many closing tags
2227                        if ( $stacksize <= 0 ) {
2228                                $tag = '';
2229                                // or close to be safe $tag = '/' . $tag;
2230                        }
2231                        // if stacktop value = tag close value then pop
2232                        elseif ( $tagstack[$stacksize - 1] == $tag ) { // found closing tag
2233                                $tag = '</' . $tag . '>'; // Close Tag
2234                                // Pop
2235                                array_pop( $tagstack );
2236                                $stacksize--;
2237                        } else { // closing tag not at top, search for it
2238                                for ( $j = $stacksize-1; $j >= 0; $j-- ) {
2239                                        if ( $tagstack[$j] == $tag ) {
2240                                        // add tag to tagqueue
2241                                                for ( $k = $stacksize-1; $k >= $j; $k--) {
2242                                                        $tagqueue .= '</' . array_pop( $tagstack ) . '>';
2243                                                        $stacksize--;
2244                                                }
2245                                                break;
2246                                        }
2247                                }
2248                                $tag = '';
2249                        }
2250                } else { // Begin Tag
2251                        $tag = strtolower($regex[1]);
2252
2253                        // Tag Cleaning
2254
2255                        // If it's an empty tag "< >", do nothing
2256                        if ( '' == $tag ) {
2257                                // do nothing
2258                        }
2259                        // ElseIf it presents itself as a self-closing tag...
2260                        elseif ( substr( $regex[2], -1 ) == '/' ) {
2261                                // ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
2262                                // immediately close it with a closing tag (the tag will encapsulate no text as a result)
2263                                if ( ! in_array( $tag, $single_tags ) )
2264                                        $regex[2] = trim( substr( $regex[2], 0, -1 ) ) . "></$tag";
2265                        }
2266                        // ElseIf it's a known single-entity tag but it doesn't close itself, do so
2267                        elseif ( in_array($tag, $single_tags) ) {
2268                                $regex[2] .= '/';
2269                        }
2270                        // Else it's not a single-entity tag
2271                        else {
2272                                // If the top of the stack is the same as the tag we want to push, close previous tag
2273                                if ( $stacksize > 0 && !in_array($tag, $nestable_tags) && $tagstack[$stacksize - 1] == $tag ) {
2274                                        $tagqueue = '</' . array_pop( $tagstack ) . '>';
2275                                        $stacksize--;
2276                                }
2277                                $stacksize = array_push( $tagstack, $tag );
2278                        }
2279
2280                        // Attributes
2281                        $attributes = $regex[2];
2282                        if ( ! empty( $attributes ) && $attributes[0] != '>' )
2283                                $attributes = ' ' . $attributes;
2284
2285                        $tag = '<' . $tag . $attributes . '>';
2286                        //If already queuing a close tag, then put this tag on, too
2287                        if ( !empty($tagqueue) ) {
2288                                $tagqueue .= $tag;
2289                                $tag = '';
2290                        }
2291                }
2292                $newtext .= substr($text, 0, $i) . $tag;
2293                $text = substr($text, $i + $l);
2294        }
2295
2296        // Clear Tag Queue
2297        $newtext .= $tagqueue;
2298
2299        // Add Remaining text
2300        $newtext .= $text;
2301
2302        // Empty Stack
2303        while( $x = array_pop($tagstack) )
2304                $newtext .= '</' . $x . '>'; // Add remaining tags to close
2305
2306        // WP fix for the bug with HTML comments
2307        $newtext = str_replace("< !--","<!--",$newtext);
2308        $newtext = str_replace("<    !--","< !--",$newtext);
2309
2310        return $newtext;
2311}
2312
2313/**
2314 * Acts on text which is about to be edited.
2315 *
2316 * The $content is run through esc_textarea(), which uses htmlspecialchars()
2317 * to convert special characters to HTML entities. If `$richedit` is set to true,
2318 * it is simply a holder for the {@see 'format_to_edit'} filter.
2319 *
2320 * @since 0.71
2321 * @since 4.4.0 The `$richedit` parameter was renamed to `$rich_text` for clarity.
2322 *
2323 * @param string $content   The text about to be edited.
2324 * @param bool   $rich_text Optional. Whether `$content` should be considered rich text,
2325 *                          in which case it would not be passed through esc_textarea().
2326 *                          Default false.
2327 * @return string The text after the filter (and possibly htmlspecialchars()) has been run.
2328 */
2329function format_to_edit( $content, $rich_text = false ) {
2330        /**
2331         * Filters the text to be formatted for editing.
2332         *
2333         * @since 1.2.0
2334         *
2335         * @param string $content The text, prior to formatting for editing.
2336         */
2337        $content = apply_filters( 'format_to_edit', $content );
2338        if ( ! $rich_text )
2339                $content = esc_textarea( $content );
2340        return $content;
2341}
2342
2343/**
2344 * Add leading zeros when necessary.
2345 *
2346 * If you set the threshold to '4' and the number is '10', then you will get
2347 * back '0010'. If you set the threshold to '4' and the number is '5000', then you
2348 * will get back '5000'.
2349 *
2350 * Uses sprintf to append the amount of zeros based on the $threshold parameter
2351 * and the size of the number. If the number is large enough, then no zeros will
2352 * be appended.
2353 *
2354 * @since 0.71
2355 *
2356 * @param int $number     Number to append zeros to if not greater than threshold.
2357 * @param int $threshold  Digit places number needs to be to not have zeros added.
2358 * @return string Adds leading zeros to number if needed.
2359 */
2360function zeroise( $number, $threshold ) {
2361        return sprintf( '%0' . $threshold . 's', $number );
2362}
2363
2364/**
2365 * Adds backslashes before letters and before a number at the start of a string.
2366 *
2367 * @since 0.71
2368 *
2369 * @param string $string Value to which backslashes will be added.
2370 * @return string String with backslashes inserted.
2371 */
2372function backslashit( $string ) {
2373        if ( isset( $string[0] ) && $string[0] >= '0' && $string[0] <= '9' )
2374                $string = '\\\\' . $string;
2375        return addcslashes( $string, 'A..Za..z' );
2376}
2377
2378/**
2379 * Appends a trailing slash.
2380 *
2381 * Will remove trailing forward and backslashes if it exists already before adding
2382 * a trailing forward slash. This prevents double slashing a string or path.
2383 *
2384 * The primary use of this is for paths and thus should be used for paths. It is
2385 * not restricted to paths and offers no specific path support.
2386 *
2387 * @since 1.2.0
2388 *
2389 * @param string $string What to add the trailing slash to.
2390 * @return string String with trailing slash added.
2391 */
2392function trailingslashit( $string ) {
2393        return untrailingslashit( $string ) . '/';
2394}
2395
2396/**
2397 * Removes trailing forward slashes and backslashes if they exist.
2398 *
2399 * The primary use of this is for paths and thus should be used for paths. It is
2400 * not restricted to paths and offers no specific path support.
2401 *
2402 * @since 2.2.0
2403 *
2404 * @param string $string What to remove the trailing slashes from.
2405 * @return string String without the trailing slashes.
2406 */
2407function untrailingslashit( $string ) {
2408        return rtrim( $string, '/\\' );
2409}
2410
2411/**
2412 * Adds slashes to escape strings.
2413 *
2414 * Slashes will first be removed if magic_quotes_gpc is set, see {@link
2415 * https://secure.php.net/magic_quotes} for more details.
2416 *
2417 * @since 0.71
2418 *
2419 * @param string $gpc The string returned from HTTP request data.
2420 * @return string Returns a string escaped with slashes.
2421 */
2422function addslashes_gpc($gpc) {
2423        if ( get_magic_quotes_gpc() )
2424                $gpc = stripslashes($gpc);
2425
2426        return wp_slash($gpc);
2427}
2428
2429/**
2430 * Navigates through an array, object, or scalar, and removes slashes from the values.
2431 *
2432 * @since 2.0.0
2433 *
2434 * @param mixed $value The value to be stripped.
2435 * @return mixed Stripped value.
2436 */
2437function stripslashes_deep( $value ) {
2438        return map_deep( $value, 'stripslashes_from_strings_only' );
2439}
2440
2441/**
2442 * Callback function for `stripslashes_deep()` which strips slashes from strings.
2443 *
2444 * @since 4.4.0
2445 *
2446 * @param mixed $value The array or string to be stripped.
2447 * @return mixed $value The stripped value.
2448 */
2449function stripslashes_from_strings_only( $value ) {
2450        return is_string( $value ) ? stripslashes( $value ) : $value;
2451}
2452
2453/**
2454 * Navigates through an array, object, or scalar, and encodes the values to be used in a URL.
2455 *
2456 * @since 2.2.0
2457 *
2458 * @param mixed $value The array or string to be encoded.
2459 * @return mixed $value The encoded value.
2460 */
2461function urlencode_deep( $value ) {
2462        return map_deep( $value, 'urlencode' );
2463}
2464
2465/**
2466 * Navigates through an array, object, or scalar, and raw-encodes the values to be used in a URL.
2467 *
2468 * @since 3.4.0
2469 *
2470 * @param mixed $value The array or string to be encoded.
2471 * @return mixed $value The encoded value.
2472 */
2473function rawurlencode_deep( $value ) {
2474        return map_deep( $value, 'rawurlencode' );
2475}
2476
2477/**
2478 * Navigates through an array, object, or scalar, and decodes URL-encoded values
2479 *
2480 * @since 4.4.0
2481 *
2482 * @param mixed $value The array or string to be decoded.
2483 * @return mixed $value The decoded value.
2484 */
2485function urldecode_deep( $value ) {
2486        return map_deep( $value, 'urldecode' );
2487}
2488
2489/**
2490 * Converts email addresses characters to HTML entities to block spam bots.
2491 *
2492 * @since 0.71
2493 *
2494 * @param string $email_address Email address.
2495 * @param int    $hex_encoding  Optional. Set to 1 to enable hex encoding.
2496 * @return string Converted email address.
2497 */
2498function antispambot( $email_address, $hex_encoding = 0 ) {
2499        $email_no_spam_address = '';
2500        for ( $i = 0, $len = strlen( $email_address ); $i < $len; $i++ ) {
2501                $j = rand( 0, 1 + $hex_encoding );
2502                if ( $j == 0 ) {
2503                        $email_no_spam_address .= '&#' . ord( $email_address[$i] ) . ';';
2504                } elseif ( $j == 1 ) {
2505                        $email_no_spam_address .= $email_address[$i];
2506                } elseif ( $j == 2 ) {
2507                        $email_no_spam_address .= '%' . zeroise( dechex( ord( $email_address[$i] ) ), 2 );
2508                }
2509        }
2510
2511        return str_replace( '@', '&#64;', $email_no_spam_address );
2512}
2513
2514/**
2515 * Callback to convert URI match to HTML A element.
2516 *
2517 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2518 *
2519 * @since 2.3.2
2520 * @access private
2521 *
2522 * @param array $matches Single Regex Match.
2523 * @return string HTML A element with URI address.
2524 */
2525function _make_url_clickable_cb( $matches ) {
2526        $url = $matches[2];
2527
2528        if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
2529                // If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
2530                // Then we can let the parenthesis balancer do its thing below.
2531                $url .= $matches[3];
2532                $suffix = '';
2533        } else {
2534                $suffix = $matches[3];
2535        }
2536
2537        // Include parentheses in the URL only if paired
2538        while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
2539                $suffix = strrchr( $url, ')' ) . $suffix;
2540                $url = substr( $url, 0, strrpos( $url, ')' ) );
2541        }
2542
2543        $url = esc_url($url);
2544        if ( empty($url) )
2545                return $matches[0];
2546
2547        return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
2548}
2549
2550/**
2551 * Callback to convert URL match to HTML A element.
2552 *
2553 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2554 *
2555 * @since 2.3.2
2556 * @access private
2557 *
2558 * @param array $matches Single Regex Match.
2559 * @return string HTML A element with URL address.
2560 */
2561function _make_web_ftp_clickable_cb( $matches ) {
2562        $ret = '';
2563        $dest = $matches[2];
2564        $dest = 'http://' . $dest;
2565
2566        // removed trailing [.,;:)] from URL
2567        if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
2568                $ret = substr($dest, -1);
2569                $dest = substr($dest, 0, strlen($dest)-1);
2570        }
2571
2572        $dest = esc_url($dest);
2573        if ( empty($dest) )
2574                return $matches[0];
2575
2576        return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
2577}
2578
2579/**
2580 * Callback to convert email address match to HTML A element.
2581 *
2582 * This function was backported from 2.5.0 to 2.3.2. Regex callback for make_clickable().
2583 *
2584 * @since 2.3.2
2585 * @access private
2586 *
2587 * @param array $matches Single Regex Match.
2588 * @return string HTML A element with email address.
2589 */
2590function _make_email_clickable_cb( $matches ) {
2591        $email = $matches[2] . '@' . $matches[3];
2592        return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
2593}
2594
2595/**
2596 * Convert plaintext URI to HTML links.
2597 *
2598 * Converts URI, www and ftp, and email addresses. Finishes by fixing links
2599 * within links.
2600 *
2601 * @since 0.71
2602 *
2603 * @param string $text Content to convert URIs.
2604 * @return string Content with converted URIs.
2605 */
2606function make_clickable( $text ) {
2607        $r = '';
2608        $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
2609        $nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code>
2610        foreach ( $textarr as $piece ) {
2611
2612                if ( preg_match( '|^<code[\s>]|i', $piece ) || preg_match( '|^<pre[\s>]|i', $piece ) || preg_match( '|^<script[\s>]|i', $piece ) || preg_match( '|^<style[\s>]|i', $piece ) )
2613                        $nested_code_pre++;
2614                elseif ( $nested_code_pre && ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) || '</script>' === strtolower( $piece ) || '</style>' === strtolower( $piece ) ) )
2615                        $nested_code_pre--;
2616
2617                if ( $nested_code_pre || empty( $piece ) || ( $piece[0] === '<' && ! preg_match( '|^<\s*[\w]{1,20}+://|', $piece ) ) ) {
2618                        $r .= $piece;
2619                        continue;
2620                }
2621
2622                // Long strings might contain expensive edge cases ...
2623                if ( 10000 < strlen( $piece ) ) {
2624                        // ... break it up
2625                        foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
2626                                if ( 2101 < strlen( $chunk ) ) {
2627                                        $r .= $chunk; // Too big, no whitespace: bail.
2628                                } else {
2629                                        $r .= make_clickable( $chunk );
2630                                }
2631                        }
2632                } else {
2633                        $ret = " $piece "; // Pad with whitespace to simplify the regexes
2634
2635                        $url_clickable = '~
2636                                ([\\s(<.,;:!?])                                        # 1: Leading whitespace, or punctuation
2637                                (                                                      # 2: URL
2638                                        [\\w]{1,20}+://                                # Scheme and hier-part prefix
2639                                        (?=\S{1,2000}\s)                               # Limit to URLs less than about 2000 characters long
2640                                        [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+         # Non-punctuation URL character
2641                                        (?:                                            # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
2642                                                [\'.,;:!?)]                            # Punctuation URL character
2643                                                [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
2644                                        )*
2645                                )
2646                                (\)?)                                                  # 3: Trailing closing parenthesis (for parethesis balancing post processing)
2647                        ~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
2648                              // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
2649
2650                        $ret = preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $ret );
2651
2652                        $ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
2653                        $ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
2654
2655                        $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
2656                        $r .= $ret;
2657                }
2658        }
2659
2660        // Cleanup of accidental links within links
2661        return preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
2662}
2663
2664/**
2665 * Breaks a string into chunks by splitting at whitespace characters.
2666 * The length of each returned chunk is as close to the specified length goal as possible,
2667 * with the caveat that each chunk includes its trailing delimiter.
2668 * Chunks longer than the goal are guaranteed to not have any inner whitespace.
2669 *
2670 * Joining the returned chunks with empty delimiters reconstructs the input string losslessly.
2671 *
2672 * Input string must have no null characters (or eventual transformations on output chunks must not care about null characters)
2673 *
2674 *     _split_str_by_whitespace( "1234 67890 1234 67890a cd 1234   890 123456789 1234567890a    45678   1 3 5 7 90 ", 10 ) ==
2675 *     array (
2676 *         0 => '1234 67890 ',  // 11 characters: Perfect split
2677 *         1 => '1234 ',        //  5 characters: '1234 67890a' was too long
2678 *         2 => '67890a cd ',   // 10 characters: '67890a cd 1234' was too long
2679 *         3 => '1234   890 ',  // 11 characters: Perfect split
2680 *         4 => '123456789 ',   // 10 characters: '123456789 1234567890a' was too long
2681 *         5 => '1234567890a ', // 12 characters: Too long, but no inner whitespace on which to split
2682 *         6 => '   45678   ',  // 11 characters: Perfect split
2683 *         7 => '1 3 5 7 90 ',  // 11 characters: End of $string
2684 *     );
2685 *
2686 * @since 3.4.0
2687 * @access private
2688 *
2689 * @param string $string The string to split.
2690 * @param int    $goal   The desired chunk length.
2691 * @return array Numeric array of chunks.
2692 */
2693function _split_str_by_whitespace( $string, $goal ) {
2694        $chunks = array();
2695
2696        $string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
2697
2698        while ( $goal < strlen( $string_nullspace ) ) {
2699                $pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
2700
2701                if ( false === $pos ) {
2702                        $pos = strpos( $string_nullspace, "\000", $goal + 1 );
2703                        if ( false === $pos ) {
2704                                break;
2705                        }
2706                }
2707
2708                $chunks[] = substr( $string, 0, $pos + 1 );
2709                $string = substr( $string, $pos + 1 );
2710                $string_nullspace = substr( $string_nullspace, $pos + 1 );
2711        }
2712
2713        if ( $string ) {
2714                $chunks[] = $string;
2715        }
2716
2717        return $chunks;
2718}
2719
2720/**
2721 * Adds rel nofollow string to all HTML A elements in content.
2722 *
2723 * @since 1.5.0
2724 *
2725 * @param string $text Content that may contain HTML A elements.
2726 * @return string Converted content.
2727 */
2728function wp_rel_nofollow( $text ) {
2729        // This is a pre save filter, so text is already escaped.
2730        $text = stripslashes($text);
2731        $text = preg_replace_callback('|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text);
2732        return wp_slash( $text );
2733}
2734
2735/**
2736 * Callback to add rel=nofollow string to HTML A element.
2737 *
2738 * Will remove already existing rel="nofollow" and rel='nofollow' from the
2739 * string to prevent from invalidating (X)HTML.
2740 *
2741 * @since 2.3.0
2742 *
2743 * @param array $matches Single Match
2744 * @return string HTML A Element with rel nofollow.
2745 */
2746function wp_rel_nofollow_callback( $matches ) {
2747        $text = $matches[1];
2748        $atts = shortcode_parse_atts( $matches[1] );
2749        $rel  = 'nofollow';
2750
2751        if ( preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'http' ) ) . ')%i', $text ) ||
2752             preg_match( '%href=["\'](' . preg_quote( set_url_scheme( home_url(), 'https' ) ) . ')%i', $text )
2753        ) {
2754                return "<a $text>";
2755        }
2756
2757        if ( ! empty( $atts['rel'] ) ) {
2758                $parts = array_map( 'trim', explode( ' ', $atts['rel'] ) );
2759                if ( false === array_search( 'nofollow', $parts ) ) {
2760                        $parts[] = 'nofollow';
2761                }
2762                $rel = implode( ' ', $parts );
2763                unset( $atts['rel'] );
2764
2765                $html = '';
2766                foreach ( $atts as $name => $value ) {
2767                        $html .= "{$name}=\"$value\" ";
2768                }
2769                $text = trim( $html );
2770        }
2771        return "<a $text rel=\"$rel\">";
2772}
2773
2774/**
2775 * Convert one smiley code to the icon graphic file equivalent.
2776 *
2777 * Callback handler for convert_smilies().
2778 *
2779 * Looks up one smiley code in the $wpsmiliestrans global array and returns an
2780 * `<img>` string for that smiley.
2781 *
2782 * @since 2.8.0
2783 *
2784 * @global array $wpsmiliestrans
2785 *
2786 * @param array $matches Single match. Smiley code to convert to image.
2787 * @return string Image string for smiley.
2788 */
2789function translate_smiley( $matches ) {
2790        global $wpsmiliestrans;
2791
2792        if ( count( $matches ) == 0 )
2793                return '';
2794
2795        $smiley = trim( reset( $matches ) );
2796        $img = $wpsmiliestrans[ $smiley ];
2797
2798        $matches = array();
2799        $ext = preg_match( '/\.([^.]+)$/', $img, $matches ) ? strtolower( $matches[1] ) : false;
2800        $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );
2801
2802        // Don't convert smilies that aren't images - they're probably emoji.
2803        if ( ! in_array( $ext, $image_exts ) ) {
2804                return $img;
2805        }
2806
2807        /**
2808         * Filters the Smiley image URL before it's used in the image element.
2809         *
2810         * @since 2.9.0
2811         *
2812         * @param string $smiley_url URL for the smiley image.
2813         * @param string $img        Filename for the smiley image.
2814         * @param string $site_url   Site URL, as returned by site_url().
2815         */
2816        $src_url = apply_filters( 'smilies_src', includes_url( "images/smilies/$img" ), $img, site_url() );
2817
2818        return sprintf( '<img src="%s" alt="%s" class="wp-smiley" style="height: 1em; max-height: 1em;" />', esc_url( $src_url ), esc_attr( $smiley ) );
2819}
2820
2821/**
2822 * Convert text equivalent of smilies to images.
2823 *
2824 * Will only convert smilies if the option 'use_smilies' is true and the global
2825 * used in the function isn't empty.
2826 *
2827 * @since 0.71
2828 *
2829 * @global string|array $wp_smiliessearch
2830 *
2831 * @param string $text Content to convert smilies from text.
2832 * @return string Converted content with text smilies replaced with images.
2833 */
2834function convert_smilies( $text ) {
2835        global $wp_smiliessearch;
2836        $output = '';
2837        if ( get_option( 'use_smilies' ) && ! empty( $wp_smiliessearch ) ) {
2838                // HTML loop taken from texturize function, could possible be consolidated
2839                $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // capture the tags as well as in between
2840                $stop = count( $textarr );// loop stuff
2841
2842                // Ignore proessing of specific tags
2843                $tags_to_ignore = 'code|pre|style|script|textarea';
2844                $ignore_block_element = '';
2845
2846                for ( $i = 0; $i < $stop; $i++ ) {
2847                        $content = $textarr[$i];
2848
2849                        // If we're in an ignore block, wait until we find its closing tag
2850                        if ( '' == $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) )  {
2851                                $ignore_block_element = $matches[1];
2852                        }
2853
2854                        // If it's not a tag and not in ignore block
2855                        if ( '' ==  $ignore_block_element && strlen( $content ) > 0 && '<' != $content[0] ) {
2856                                $content = preg_replace_callback( $wp_smiliessearch, 'translate_smiley', $content );
2857                        }
2858
2859                        // did we exit ignore block
2860                        if ( '' != $ignore_block_element && '</' . $ignore_block_element . '>' == $content )  {
2861                                $ignore_block_element = '';
2862                        }
2863
2864                        $output .= $content;
2865                }
2866        } else {
2867                // return default text.
2868                $output = $text;
2869        }
2870        return $output;
2871}
2872
2873/**
2874 * Verifies that an email is valid.
2875 *
2876 * Does not grok i18n domains. Not RFC compliant.
2877 *
2878 * @since 0.71
2879 *
2880 * @param string $email      Email address to verify.
2881 * @param bool   $deprecated Deprecated.
2882 * @return string|bool Either false or the valid email address.
2883 */
2884function is_email( $email, $deprecated = false ) {
2885        if ( ! empty( $deprecated ) )
2886                _deprecated_argument( __FUNCTION__, '3.0.0' );
2887
2888        // Test for the minimum length the email can be
2889        if ( strlen( $email ) < 6 ) {
2890                /**
2891                 * Filters whether an email address is valid.
2892                 *
2893                 * This filter is evaluated under several different contexts, such as 'email_too_short',
2894                 * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits',
2895                 * 'domain_no_periods', 'sub_hyphen_limits', 'sub_invalid_chars', or no specific context.
2896                 *
2897                 * @since 2.8.0
2898                 *
2899                 * @param bool   $is_email Whether the email address has passed the is_email() checks. Default false.
2900                 * @param string $email    The email address being checked.
2901                 * @param string $context  Context under which the email was tested.
2902                 */
2903                return apply_filters( 'is_email', false, $email, 'email_too_short' );
2904        }
2905
2906        // Test for an @ character after the first position
2907        if ( strpos( $email, '@', 1 ) === false ) {
2908                /** This filter is documented in wp-includes/formatting.php */
2909                return apply_filters( 'is_email', false, $email, 'email_no_at' );
2910        }
2911
2912        // Split out the local and domain parts
2913        list( $local, $domain ) = explode( '@', $email, 2 );
2914
2915        // LOCAL PART
2916        // Test for invalid characters
2917        if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) {
2918                /** This filter is documented in wp-includes/formatting.php */
2919                return apply_filters( 'is_email', false, $email, 'local_invalid_chars' );
2920        }
2921
2922        // DOMAIN PART
2923        // Test for sequences of periods
2924        if ( preg_match( '/\.{2,}/', $domain ) ) {
2925                /** This filter is documented in wp-includes/formatting.php */
2926                return apply_filters( 'is_email', false, $email, 'domain_period_sequence' );
2927        }
2928
2929        // Test for leading and trailing periods and whitespace
2930        if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) {
2931                /** This filter is documented in wp-includes/formatting.php */
2932                return apply_filters( 'is_email', false, $email, 'domain_period_limits' );
2933        }
2934
2935        // Split the domain into subs
2936        $subs = explode( '.', $domain );
2937
2938        // Assume the domain will have at least two subs
2939        if ( 2 > count( $subs ) ) {
2940                /** This filter is documented in wp-includes/formatting.php */
2941                return apply_filters( 'is_email', false, $email, 'domain_no_periods' );
2942        }
2943
2944        // Loop through each sub
2945        foreach ( $subs as $sub ) {
2946                // Test for leading and trailing hyphens and whitespace
2947                if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) {
2948                        /** This filter is documented in wp-includes/formatting.php */
2949                        return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' );
2950                }
2951
2952                // Test for invalid characters
2953                if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) {
2954                        /** This filter is documented in wp-includes/formatting.php */
2955                        return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' );
2956                }
2957        }
2958
2959        // Congratulations your email made it!
2960        /** This filter is documented in wp-includes/formatting.php */
2961        return apply_filters( 'is_email', $email, $email, null );
2962}
2963
2964/**
2965 * Convert to ASCII from email subjects.
2966 *
2967 * @since 1.2.0
2968 *
2969 * @param string $string Subject line
2970 * @return string Converted string to ASCII
2971 */
2972function wp_iso_descrambler( $string ) {
2973        /* this may only work with iso-8859-1, I'm afraid */
2974        if (!preg_match('#\=\?(.+)\?Q\?(.+)\?\=#i', $string, $matches)) {
2975                return $string;
2976        } else {
2977                $subject = str_replace('_', ' ', $matches[2]);
2978                return preg_replace_callback( '#\=([0-9a-f]{2})#i', '_wp_iso_convert', $subject );
2979        }
2980}
2981
2982/**
2983 * Helper function to convert hex encoded chars to ASCII
2984 *
2985 * @since 3.1.0
2986 * @access private
2987 *
2988 * @param array $match The preg_replace_callback matches array
2989 * @return string Converted chars
2990 */
2991function _wp_iso_convert( $match ) {
2992        return chr( hexdec( strtolower( $match[1] ) ) );
2993}
2994
2995/**
2996 * Returns a date in the GMT equivalent.
2997 *
2998 * Requires and returns a date in the Y-m-d H:i:s format. If there is a
2999 * timezone_string available, the date is assumed to be in that timezone,
3000 * otherwise it simply subtracts the value of the 'gmt_offset' option. Return
3001 * format can be overridden using the $format parameter.
3002 *
3003 * @since 1.2.0
3004 *
3005 * @param string $string The date to be converted.
3006 * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
3007 * @return string GMT version of the date provided.
3008 */
3009function get_gmt_from_date( $string, $format = 'Y-m-d H:i:s' ) {
3010        $tz = get_option( 'timezone_string' );
3011        if ( $tz ) {
3012                $datetime = date_create( $string, new DateTimeZone( $tz ) );
3013                if ( ! $datetime ) {
3014                        return gmdate( $format, 0 );
3015                }
3016                $datetime->setTimezone( new DateTimeZone( 'UTC' ) );
3017                $string_gmt = $datetime->format( $format );
3018        } else {
3019                if ( ! preg_match( '#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches ) ) {
3020                        $datetime = strtotime( $string );
3021                        if ( false === $datetime ) {
3022                                return gmdate( $format, 0 );
3023                        }
3024                        return gmdate( $format, $datetime );
3025                }
3026                $string_time = gmmktime( $matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1] );
3027                $string_gmt = gmdate( $format, $string_time - get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
3028        }
3029        return $string_gmt;
3030}
3031
3032/**
3033 * Converts a GMT date into the correct format for the blog.
3034 *
3035 * Requires and returns a date in the Y-m-d H:i:s format. If there is a
3036 * timezone_string available, the returned date is in that timezone, otherwise
3037 * it simply adds the value of gmt_offset. Return format can be overridden
3038 * using the $format parameter
3039 *
3040 * @since 1.2.0
3041 *
3042 * @param string $string The date to be converted.
3043 * @param string $format The format string for the returned date (default is Y-m-d H:i:s)
3044 * @return string Formatted date relative to the timezone / GMT offset.
3045 */
3046function get_date_from_gmt( $string, $format = 'Y-m-d H:i:s' ) {
3047        $tz = get_option( 'timezone_string' );
3048        if ( $tz ) {
3049                $datetime = date_create( $string, new DateTimeZone( 'UTC' ) );
3050                if ( ! $datetime )
3051                        return date( $format, 0 );
3052                $datetime->setTimezone( new DateTimeZone( $tz ) );
3053                $string_localtime = $datetime->format( $format );
3054        } else {
3055                if ( ! preg_match('#([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})#', $string, $matches) )
3056                        return date( $format, 0 );
3057                $string_time = gmmktime( $matches[4], $matches[5], $matches[6], $matches[2], $matches[3], $matches[1] );
3058                $string_localtime = gmdate( $format, $string_time + get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
3059        }
3060        return $string_localtime;
3061}
3062
3063/**
3064 * Computes an offset in seconds from an iso8601 timezone.
3065 *
3066 * @since 1.5.0
3067 *
3068 * @param string $timezone Either 'Z' for 0 offset or '±hhmm'.
3069 * @return int|float The offset in seconds.
3070 */
3071function iso8601_timezone_to_offset( $timezone ) {
3072        // $timezone is either 'Z' or '[+|-]hhmm'
3073        if ($timezone == 'Z') {
3074                $offset = 0;
3075        } else {
3076                $sign    = (substr($timezone, 0, 1) == '+') ? 1 : -1;
3077                $hours   = intval(substr($timezone, 1, 2));
3078                $minutes = intval(substr($timezone, 3, 4)) / 60;
3079                $offset  = $sign * HOUR_IN_SECONDS * ($hours + $minutes);
3080        }
3081        return $offset;
3082}
3083
3084/**
3085 * Converts an iso8601 date to MySQL DateTime format used by post_date[_gmt].
3086 *
3087 * @since 1.5.0
3088 *
3089 * @param string $date_string Date and time in ISO 8601 format {@link https://en.wikipedia.org/wiki/ISO_8601}.
3090 * @param string $timezone    Optional. If set to GMT returns the time minus gmt_offset. Default is 'user'.
3091 * @return string The date and time in MySQL DateTime format - Y-m-d H:i:s.
3092 */
3093function iso8601_to_datetime( $date_string, $timezone = 'user' ) {
3094        $timezone = strtolower($timezone);
3095
3096        if ($timezone == 'gmt') {
3097
3098                preg_match('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', $date_string, $date_bits);
3099
3100                if (!empty($date_bits[7])) { // we have a timezone, so let's compute an offset
3101                        $offset = iso8601_timezone_to_offset($date_bits[7]);
3102                } else { // we don't have a timezone, so we assume user local timezone (not server's!)
3103                        $offset = HOUR_IN_SECONDS * get_option('gmt_offset');
3104                }
3105
3106                $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
3107                $timestamp -= $offset;
3108
3109                return gmdate('Y-m-d H:i:s', $timestamp);
3110
3111        } elseif ($timezone == 'user') {
3112                return preg_replace('#([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(Z|[\+|\-][0-9]{2,4}){0,1}#', '$1-$2-$3 $4:$5:$6', $date_string);
3113        }
3114}
3115
3116/**
3117 * Strips out all characters that are not allowable in an email.
3118 *
3119 * @since 1.5.0
3120 *
3121 * @param string $email Email address to filter.
3122 * @return string Filtered email address.
3123 */
3124function sanitize_email( $email ) {
3125        // Test for the minimum length the email can be
3126        if ( strlen( $email ) < 6 ) {
3127                /**
3128                 * Filters a sanitized email address.
3129                 *
3130                 * This filter is evaluated under several contexts, including 'email_too_short',
3131                 * 'email_no_at', 'local_invalid_chars', 'domain_period_sequence', 'domain_period_limits',
3132                 * 'domain_no_periods', 'domain_no_valid_subs', or no context.
3133                 *
3134                 * @since 2.8.0
3135                 *
3136                 * @param string $email   The sanitized email address.
3137                 * @param string $email   The email address, as provided to sanitize_email().
3138                 * @param string $message A message to pass to the user.
3139                 */
3140                return apply_filters( 'sanitize_email', '', $email, 'email_too_short' );
3141        }
3142
3143        // Test for an @ character after the first position
3144        if ( strpos( $email, '@', 1 ) === false ) {
3145                /** This filter is documented in wp-includes/formatting.php */
3146                return apply_filters( 'sanitize_email', '', $email, 'email_no_at' );
3147        }
3148
3149        // Split out the local and domain parts
3150        list( $local, $domain ) = explode( '@', $email, 2 );
3151
3152        // LOCAL PART
3153        // Test for invalid characters
3154        $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
3155        if ( '' === $local ) {
3156                /** This filter is documented in wp-includes/formatting.php */
3157                return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
3158        }
3159
3160        // DOMAIN PART
3161        // Test for sequences of periods
3162        $domain = preg_replace( '/\.{2,}/', '', $domain );
3163        if ( '' === $domain ) {
3164                /** This filter is documented in wp-includes/formatting.php */
3165                return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
3166        }
3167
3168        // Test for leading and trailing periods and whitespace
3169        $domain = trim( $domain, " \t\n\r\0\x0B." );
3170        if ( '' === $domain ) {
3171                /** This filter is documented in wp-includes/formatting.php */
3172                return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' );
3173        }
3174
3175        // Split the domain into subs
3176        $subs = explode( '.', $domain );
3177
3178        // Assume the domain will have at least two subs
3179        if ( 2 > count( $subs ) ) {
3180                /** This filter is documented in wp-includes/formatting.php */
3181                return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' );
3182        }
3183
3184        // Create an array that will contain valid subs
3185        $new_subs = array();
3186
3187        // Loop through each sub
3188        foreach ( $subs as $sub ) {
3189                // Test for leading and trailing hyphens
3190                $sub = trim( $sub, " \t\n\r\0\x0B-" );
3191
3192                // Test for invalid characters
3193                $sub = preg_replace( '/[^a-z0-9-]+/i', '', $sub );
3194
3195                // If there's anything left, add it to the valid subs
3196                if ( '' !== $sub ) {
3197                        $new_subs[] = $sub;
3198                }
3199        }
3200
3201        // If there aren't 2 or more valid subs
3202        if ( 2 > count( $new_subs ) ) {
3203                /** This filter is documented in wp-includes/formatting.php */
3204                return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' );
3205        }
3206
3207        // Join valid subs into the new domain
3208        $domain = join( '.', $new_subs );
3209
3210        // Put the email back together
3211        $email = $local . '@' . $domain;
3212
3213        // Congratulations your email made it!
3214        /** This filter is documented in wp-includes/formatting.php */
3215        return apply_filters( 'sanitize_email', $email, $email, null );
3216}
3217
3218/**
3219 * Determines the difference between two timestamps.
3220 *
3221 * The difference is returned in a human readable format such as "1 hour",
3222 * "5 mins", "2 days".
3223 *
3224 * @since 1.5.0
3225 *
3226 * @param int $from Unix timestamp from which the difference begins.
3227 * @param int $to   Optional. Unix timestamp to end the time difference. Default becomes time() if not set.
3228 * @return string Human readable time difference.
3229 */
3230function human_time_diff( $from, $to = '' ) {
3231        if ( empty( $to ) ) {
3232                $to = time();
3233        }
3234
3235        $diff = (int) abs( $to - $from );
3236
3237        if ( $diff < HOUR_IN_SECONDS ) {
3238                $mins = round( $diff / MINUTE_IN_SECONDS );
3239                if ( $mins <= 1 )
3240                        $mins = 1;
3241                /* translators: Time difference between two dates, in minutes (min=minute). 1: Number of minutes */
3242                $since = sprintf( _n( '%s min', '%s mins', $mins ), $mins );
3243        } elseif ( $diff < DAY_IN_SECONDS && $diff >= HOUR_IN_SECONDS ) {
3244                $hours = round( $diff / HOUR_IN_SECONDS );
3245                if ( $hours <= 1 )
3246                        $hours = 1;
3247                /* translators: Time difference between two dates, in hours. 1: Number of hours */
3248                $since = sprintf( _n( '%s hour', '%s hours', $hours ), $hours );
3249        } elseif ( $diff < WEEK_IN_SECONDS && $diff >= DAY_IN_SECONDS ) {
3250                $days = round( $diff / DAY_IN_SECONDS );
3251                if ( $days <= 1 )
3252                        $days = 1;
3253                /* translators: Time difference between two dates, in days. 1: Number of days */
3254                $since = sprintf( _n( '%s day', '%s days', $days ), $days );
3255        } elseif ( $diff < MONTH_IN_SECONDS && $diff >= WEEK_IN_SECONDS ) {
3256                $weeks = round( $diff / WEEK_IN_SECONDS );
3257                if ( $weeks <= 1 )
3258                        $weeks = 1;
3259                /* translators: Time difference between two dates, in weeks. 1: Number of weeks */
3260                $since = sprintf( _n( '%s week', '%s weeks', $weeks ), $weeks );
3261        } elseif ( $diff < YEAR_IN_SECONDS && $diff >= MONTH_IN_SECONDS ) {
3262                $months = round( $diff / MONTH_IN_SECONDS );
3263                if ( $months <= 1 )
3264                        $months = 1;
3265                /* translators: Time difference between two dates, in months. 1: Number of months */
3266                $since = sprintf( _n( '%s month', '%s months', $months ), $months );
3267        } elseif ( $diff >= YEAR_IN_SECONDS ) {
3268                $years = round( $diff / YEAR_IN_SECONDS );
3269                if ( $years <= 1 )
3270                        $years = 1;
3271                /* translators: Time difference between two dates, in years. 1: Number of years */
3272                $since = sprintf( _n( '%s year', '%s years', $years ), $years );
3273        }
3274
3275        /**
3276         * Filters the human readable difference between two timestamps.
3277         *
3278         * @since 4.0.0
3279         *
3280         * @param string $since The difference in human readable text.
3281         * @param int    $diff  The difference in seconds.
3282         * @param int    $from  Unix timestamp from which the difference begins.
3283         * @param int    $to    Unix timestamp to end the time difference.
3284         */
3285        return apply_filters( 'human_time_diff', $since, $diff, $from, $to );
3286}
3287
3288/**
3289 * Generates an excerpt from the content, if needed.
3290 *
3291 * The excerpt word amount will be 55 words and if the amount is greater than
3292 * that, then the string ' [&hellip;]' will be appended to the excerpt. If the string
3293 * is less than 55 words, then the content will be returned as is.
3294 *
3295 * The 55 word limit can be modified by plugins/themes using the {@see 'excerpt_length'} filter
3296 * The ' [&hellip;]' string can be modified by plugins/themes using the {@see 'excerpt_more'} filter
3297 *
3298 * @since 1.5.0
3299 *
3300 * @param string $text Optional. The excerpt. If set to empty, an excerpt is generated.
3301 * @return string The excerpt.
3302 */
3303function wp_trim_excerpt( $text = '' ) {
3304        $raw_excerpt = $text;
3305        if ( '' == $text ) {
3306                $text = get_the_content('');
3307
3308                $text = strip_shortcodes( $text );
3309
3310                /** This filter is documented in wp-includes/post-template.php */
3311                $text = apply_filters( 'the_content', $text );
3312                $text = str_replace(']]>', ']]&gt;', $text);
3313
3314                /**
3315                 * Filters the number of words in an excerpt.
3316                 *
3317                 * @since 2.7.0
3318                 *
3319                 * @param int $number The number of words. Default 55.
3320                 */
3321                $excerpt_length = apply_filters( 'excerpt_length', 55 );
3322                /**
3323                 * Filters the string in the "more" link displayed after a trimmed excerpt.
3324                 *
3325                 * @since 2.9.0
3326                 *
3327                 * @param string $more_string The string shown within the more link.
3328                 */
3329                $excerpt_more = apply_filters( 'excerpt_more', ' ' . '[&hellip;]' );
3330                $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
3331        }
3332        /**
3333         * Filters the trimmed excerpt string.
3334         *
3335         * @since 2.8.0
3336         *
3337         * @param string $text        The trimmed text.
3338         * @param string $raw_excerpt The text prior to trimming.
3339         */
3340        return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
3341}
3342
3343/**
3344 * Trims text to a certain number of words.
3345 *
3346 * This function is localized. For languages that count 'words' by the individual
3347 * character (such as East Asian languages), the $num_words argument will apply
3348 * to the number of individual characters.
3349 *
3350 * @since 3.3.0
3351 *
3352 * @param string $text      Text to trim.
3353 * @param int    $num_words Number of words. Default 55.
3354 * @param string $more      Optional. What to append if $text needs to be trimmed. Default '&hellip;'.
3355 * @return string Trimmed text.
3356 */
3357function wp_trim_words( $text, $num_words = 55, $more = null ) {
3358        if ( null === $more ) {
3359                $more = __( '&hellip;' );
3360        }
3361
3362        $original_text = $text;
3363        $text = wp_strip_all_tags( $text );
3364
3365        /*
3366         * translators: If your word count is based on single characters (e.g. East Asian characters),
3367         * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
3368         * Do not translate into your own language.
3369         */
3370        if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
3371                $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
3372                preg_match_all( '/./u', $text, $words_array );
3373                $words_array = array_slice( $words_array[0], 0, $num_words + 1 );
3374                $sep = '';
3375        } else {
3376                $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
3377                $sep = ' ';
3378        }
3379
3380        if ( count( $words_array ) > $num_words ) {
3381                array_pop( $words_array );
3382                $text = implode( $sep, $words_array );
3383                $text = $text . $more;
3384        } else {
3385                $text = implode( $sep, $words_array );
3386        }
3387
3388        /**
3389         * Filters the text content after words have been trimmed.
3390         *
3391         * @since 3.3.0
3392         *
3393         * @param string $text          The trimmed text.
3394         * @param int    $num_words     The number of words to trim the text to. Default 55.
3395         * @param string $more          An optional string to append to the end of the trimmed text, e.g. &hellip;.
3396         * @param string $original_text The text before it was trimmed.
3397         */
3398        return apply_filters( 'wp_trim_words', $text, $num_words, $more, $original_text );
3399}
3400
3401/**
3402 * Converts named entities into numbered entities.
3403 *
3404 * @since 1.5.1
3405 *
3406 * @param string $text The text within which entities will be converted.
3407 * @return string Text with converted entities.
3408 */
3409function ent2ncr( $text ) {
3410
3411        /**
3412         * Filters text before named entities are converted into numbered entities.
3413         *
3414         * A non-null string must be returned for the filter to be evaluated.
3415         *
3416         * @since 3.3.0
3417         *
3418         * @param null   $converted_text The text to be converted. Default null.
3419         * @param string $text           The text prior to entity conversion.
3420         */
3421        $filtered = apply_filters( 'pre_ent2ncr', null, $text );
3422        if ( null !== $filtered )
3423                return $filtered;
3424
3425        $to_ncr = array(
3426                '&quot;' => '&#34;',
3427                '&amp;' => '&#38;',
3428                '&lt;' => '&#60;',
3429                '&gt;' => '&#62;',
3430                '|' => '&#124;',
3431                '&nbsp;' => '&#160;',
3432                '&iexcl;' => '&#161;',
3433                '&cent;' => '&#162;',
3434                '&pound;' => '&#163;',
3435                '&curren;' => '&#164;',
3436                '&yen;' => '&#165;',
3437                '&brvbar;' => '&#166;',
3438                '&brkbar;' => '&#166;',
3439                '&sect;' => '&#167;',
3440                '&uml;' => '&#168;',
3441                '&die;' => '&#168;',
3442                '&copy;' => '&#169;',
3443                '&ordf;' => '&#170;',
3444                '&laquo;' => '&#171;',
3445                '&not;' => '&#172;',
3446                '&shy;' => '&#173;',
3447                '&reg;' => '&#174;',
3448                '&macr;' => '&#175;',
3449                '&hibar;' => '&#175;',
3450                '&deg;' => '&#176;',
3451                '&plusmn;' => '&#177;',
3452                '&sup2;' => '&#178;',
3453                '&sup3;' => '&#179;',
3454                '&acute;' => '&#180;',
3455                '&micro;' => '&#181;',
3456                '&para;' => '&#182;',
3457                '&middot;' => '&#183;',
3458                '&cedil;' => '&#184;',
3459                '&sup1;' => '&#185;',
3460                '&ordm;' => '&#186;',
3461                '&raquo;' => '&#187;',
3462                '&frac14;' => '&#188;',
3463                '&frac12;' => '&#189;',
3464                '&frac34;' => '&#190;',
3465                '&iquest;' => '&#191;',
3466                '&Agrave;' => '&#192;',
3467                '&Aacute;' => '&#193;',
3468                '&Acirc;' => '&#194;',
3469                '&Atilde;' => '&#195;',
3470                '&Auml;' => '&#196;',
3471                '&Aring;' => '&#197;',
3472                '&AElig;' => '&#198;',
3473                '&Ccedil;' => '&#199;',
3474                '&Egrave;' => '&#200;',
3475                '&Eacute;' => '&#201;',
3476                '&Ecirc;' => '&#202;',
3477                '&Euml;' => '&#203;',
3478                '&Igrave;' => '&#204;',
3479                '&Iacute;' => '&#205;',
3480                '&Icirc;' => '&#206;',
3481                '&Iuml;' => '&#207;',
3482                '&ETH;' => '&#208;',
3483                '&Ntilde;' => '&#209;',
3484                '&Ograve;' => '&#210;',
3485                '&Oacute;' => '&#211;',
3486                '&Ocirc;' => '&#212;',
3487                '&Otilde;' => '&#213;',
3488                '&Ouml;' => '&#214;',
3489                '&times;' => '&#215;',
3490                '&Oslash;' => '&#216;',
3491                '&Ugrave;' => '&#217;',
3492                '&Uacute;' => '&#218;',
3493                '&Ucirc;' => '&#219;',
3494                '&Uuml;' => '&#220;',
3495                '&Yacute;' => '&#221;',
3496                '&THORN;' => '&#222;',
3497                '&szlig;' => '&#223;',
3498                '&agrave;' => '&#224;',
3499                '&aacute;' => '&#225;',
3500                '&acirc;' => '&#226;',
3501                '&atilde;' => '&#227;',
3502                '&auml;' => '&#228;',
3503                '&aring;' => '&#229;',
3504                '&aelig;' => '&#230;',
3505                '&ccedil;' => '&#231;',
3506                '&egrave;' => '&#232;',
3507                '&eacute;' => '&#233;',
3508                '&ecirc;' => '&#234;',
3509                '&euml;' => '&#235;',
3510                '&igrave;' => '&#236;',
3511                '&iacute;' => '&#237;',
3512                '&icirc;' => '&#238;',
3513                '&iuml;' => '&#239;',
3514                '&eth;' => '&#240;',
3515                '&ntilde;' => '&#241;',
3516                '&ograve;' => '&#242;',
3517                '&oacute;' => '&#243;',
3518                '&ocirc;' => '&#244;',
3519                '&otilde;' => '&#245;',
3520                '&ouml;' => '&#246;',
3521                '&divide;' => '&#247;',
3522                '&oslash;' => '&#248;',
3523                '&ugrave;' => '&#249;',
3524                '&uacute;' => '&#250;',
3525                '&ucirc;' => '&#251;',
3526                '&uuml;' => '&#252;',
3527                '&yacute;' => '&#253;',
3528                '&thorn;' => '&#254;',
3529                '&yuml;' => '&#255;',
3530                '&OElig;' => '&#338;',
3531                '&oelig;' => '&#339;',
3532                '&Scaron;' => '&#352;',
3533                '&scaron;' => '&#353;',
3534                '&Yuml;' => '&#376;',
3535                '&fnof;' => '&#402;',
3536                '&circ;' => '&#710;',
3537                '&tilde;' => '&#732;',
3538                '&Alpha;' => '&#913;',
3539                '&Beta;' => '&#914;',
3540                '&Gamma;' => '&#915;',
3541                '&Delta;' => '&#916;',
3542                '&Epsilon;' => '&#917;',
3543                '&Zeta;' => '&#918;',
3544                '&Eta;' => '&#919;',
3545                '&Theta;' => '&#920;',
3546                '&Iota;' => '&#921;',
3547                '&Kappa;' => '&#922;',
3548                '&Lambda;' => '&#923;',
3549                '&Mu;' => '&#924;',
3550                '&Nu;' => '&#925;',
3551                '&Xi;' => '&#926;',
3552                '&Omicron;' => '&#927;',
3553                '&Pi;' => '&#928;',
3554                '&Rho;' => '&#929;',
3555                '&Sigma;' => '&#931;',
3556                '&Tau;' => '&#932;',
3557                '&Upsilon;' => '&#933;',
3558                '&Phi;' => '&#934;',
3559                '&Chi;' => '&#935;',
3560                '&Psi;' => '&#936;',
3561                '&Omega;' => '&#937;',
3562                '&alpha;' => '&#945;',
3563                '&beta;' => '&#946;',
3564                '&gamma;' => '&#947;',
3565                '&delta;' => '&#948;',
3566                '&epsilon;' => '&#949;',
3567                '&zeta;' => '&#950;',
3568                '&eta;' => '&#951;',
3569                '&theta;' => '&#952;',
3570                '&iota;' => '&#953;',
3571                '&kappa;' => '&#954;',
3572                '&lambda;' => '&#955;',
3573                '&mu;' => '&#956;',
3574                '&nu;' => '&#957;',
3575                '&xi;' => '&#958;',
3576                '&omicron;' => '&#959;',
3577                '&pi;' => '&#960;',
3578                '&rho;' => '&#961;',
3579                '&sigmaf;' => '&#962;',
3580                '&sigma;' => '&#963;',
3581                '&tau;' => '&#964;',
3582                '&upsilon;' => '&#965;',
3583                '&phi;' => '&#966;',
3584                '&chi;' => '&#967;',
3585                '&psi;' => '&#968;',
3586                '&omega;' => '&#969;',
3587                '&thetasym;' => '&#977;',
3588                '&upsih;' => '&#978;',
3589                '&piv;' => '&#982;',
3590                '&ensp;' => '&#8194;',
3591                '&emsp;' => '&#8195;',
3592                '&thinsp;' => '&#8201;',
3593                '&zwnj;' => '&#8204;',
3594                '&zwj;' => '&#8205;',
3595                '&lrm;' => '&#8206;',
3596                '&rlm;' => '&#8207;',
3597                '&ndash;' => '&#8211;',
3598                '&mdash;' => '&#8212;',
3599                '&lsquo;' => '&#8216;',
3600                '&rsquo;' => '&#8217;',
3601                '&sbquo;' => '&#8218;',
3602                '&ldquo;' => '&#8220;',
3603                '&rdquo;' => '&#8221;',
3604                '&bdquo;' => '&#8222;',
3605                '&dagger;' => '&#8224;',
3606                '&Dagger;' => '&#8225;',
3607                '&bull;' => '&#8226;',
3608                '&hellip;' => '&#8230;',
3609                '&permil;' => '&#8240;',
3610                '&prime;' => '&#8242;',
3611                '&Prime;' => '&#8243;',
3612                '&lsaquo;' => '&#8249;',
3613                '&rsaquo;' => '&#8250;',
3614                '&oline;' => '&#8254;',
3615                '&frasl;' => '&#8260;',
3616                '&euro;' => '&#8364;',
3617                '&image;' => '&#8465;',
3618                '&weierp;' => '&#8472;',
3619                '&real;' => '&#8476;',
3620                '&trade;' => '&#8482;',
3621                '&alefsym;' => '&#8501;',
3622                '&crarr;' => '&#8629;',
3623                '&lArr;' => '&#8656;',
3624                '&uArr;' => '&#8657;',
3625                '&rArr;' => '&#8658;',
3626                '&dArr;' => '&#8659;',
3627                '&hArr;' => '&#8660;',
3628                '&forall;' => '&#8704;',
3629                '&part;' => '&#8706;',
3630                '&exist;' => '&#8707;',
3631                '&empty;' => '&#8709;',
3632                '&nabla;' => '&#8711;',
3633                '&isin;' => '&#8712;',
3634                '&notin;' => '&#8713;',
3635                '&ni;' => '&#8715;',
3636                '&prod;' => '&#8719;',
3637                '&sum;' => '&#8721;',
3638                '&minus;' => '&#8722;',
3639                '&lowast;' => '&#8727;',
3640                '&radic;' => '&#8730;',
3641                '&prop;' => '&#8733;',
3642                '&infin;' => '&#8734;',
3643                '&ang;' => '&#8736;',
3644                '&and;' => '&#8743;',
3645                '&or;' => '&#8744;',
3646                '&cap;' => '&#8745;',
3647                '&cup;' => '&#8746;',
3648                '&int;' => '&#8747;',
3649                '&there4;' => '&#8756;',
3650                '&sim;' => '&#8764;',
3651                '&cong;' => '&#8773;',
3652                '&asymp;' => '&#8776;',
3653                '&ne;' => '&#8800;',
3654                '&equiv;' => '&#8801;',
3655                '&le;' => '&#8804;',
3656                '&ge;' => '&#8805;',
3657                '&sub;' => '&#8834;',
3658                '&sup;' => '&#8835;',
3659                '&nsub;' => '&#8836;',
3660                '&sube;' => '&#8838;',
3661                '&supe;' => '&#8839;',
3662                '&oplus;' => '&#8853;',
3663                '&otimes;' => '&#8855;',
3664                '&perp;' => '&#8869;',
3665                '&sdot;' => '&#8901;',
3666                '&lceil;' => '&#8968;',
3667                '&rceil;' => '&#8969;',
3668                '&lfloor;' => '&#8970;',
3669                '&rfloor;' => '&#8971;',
3670                '&lang;' => '&#9001;',
3671                '&rang;' => '&#9002;',
3672                '&larr;' => '&#8592;',
3673                '&uarr;' => '&#8593;',
3674                '&rarr;' => '&#8594;',
3675                '&darr;' => '&#8595;',
3676                '&harr;' => '&#8596;',
3677                '&loz;' => '&#9674;',
3678                '&spades;' => '&#9824;',
3679                '&clubs;' => '&#9827;',
3680                '&hearts;' => '&#9829;',
3681                '&diams;' => '&#9830;'
3682        );
3683
3684        return str_replace( array_keys($to_ncr), array_values($to_ncr), $text );
3685}
3686
3687/**
3688 * Formats text for the editor.
3689 *
3690 * Generally the browsers treat everything inside a textarea as text, but
3691 * it is still a good idea to HTML entity encode `<`, `>` and `&` in the content.
3692 *
3693 * The filter {@see 'format_for_editor'} is applied here. If `$text` is empty the
3694 * filter will be applied to an empty string.
3695 *
3696 * @since 4.3.0
3697 *
3698 * @see _WP_Editors::editor()
3699 *
3700 * @param string $text           The text to be formatted.
3701 * @param string $default_editor The default editor for the current user.
3702 *                               It is usually either 'html' or 'tinymce'.
3703 * @return string The formatted text after filter is applied.
3704 */
3705function format_for_editor( $text, $default_editor = null ) {
3706        if ( $text ) {
3707                $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) );
3708        }
3709
3710        /**
3711         * Filters the text after it is formatted for the editor.
3712         *
3713         * @since 4.3.0
3714         *
3715         * @param string $text           The formatted text.
3716         * @param string $default_editor The default editor for the current user.
3717         *                               It is usually either 'html' or 'tinymce'.
3718         */
3719        return apply_filters( 'format_for_editor', $text, $default_editor );
3720}
3721
3722/**
3723 * Perform a deep string replace operation to ensure the values in $search are no longer present
3724 *
3725 * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
3726 * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
3727 * str_replace would return
3728 *
3729 * @since 2.8.1
3730 * @access private
3731 *
3732 * @param string|array $search  The value being searched for, otherwise known as the needle.
3733 *                              An array may be used to designate multiple needles.
3734 * @param string       $subject The string being searched and replaced on, otherwise known as the haystack.
3735 * @return string The string with the replaced svalues.
3736 */
3737function _deep_replace( $search, $subject ) {
3738        $subject = (string) $subject;
3739
3740        $count = 1;
3741        while ( $count ) {
3742                $subject = str_replace( $search, '', $subject, $count );
3743        }
3744
3745        return $subject;
3746}
3747
3748/**
3749 * Escapes data for use in a MySQL query.
3750 *
3751 * Usually you should prepare queries using wpdb::prepare().
3752 * Sometimes, spot-escaping is required or useful. One example
3753 * is preparing an array for use in an IN clause.
3754 *
3755 * @since 2.8.0
3756 *
3757 * @global wpdb $wpdb WordPress database abstraction object.
3758 *
3759 * @param string|array $data Unescaped data
3760 * @return string|array Escaped data
3761 */
3762function esc_sql( $data ) {
3763        global $wpdb;
3764        return $wpdb->_escape( $data );
3765}
3766
3767/**
3768 * Checks and cleans a URL.
3769 *
3770 * A number of characters are removed from the URL. If the URL is for displaying
3771 * (the default behaviour) ampersands are also replaced. The {@see 'clean_url'} filter
3772 * is applied to the returned cleaned URL.
3773 *
3774 * @since 2.8.0
3775 *
3776 * @param string $url       The URL to be cleaned.
3777 * @param array  $protocols Optional. An array of acceptable protocols.
3778 *                                  Defaults to return value of wp_allowed_protocols()
3779 * @param string $_context  Private. Use esc_url_raw() for database usage.
3780 * @return string The cleaned $url after the {@see 'clean_url'} filter is applied.
3781 */
3782function esc_url( $url, $protocols = null, $_context = 'display' ) {
3783        $original_url = $url;
3784
3785        if ( '' == $url )
3786                return $url;
3787
3788        $url = str_replace( ' ', '%20', $url );
3789        $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url);
3790
3791        if ( '' === $url ) {
3792                return $url;
3793        }
3794
3795        if ( 0 !== stripos( $url, 'mailto:' ) ) {
3796                $strip = array('%0d', '%0a', '%0D', '%0A');
3797                $url = _deep_replace($strip, $url);
3798        }
3799
3800        $url = str_replace(';//', '://', $url);
3801        /* If the URL doesn't appear to contain a scheme, we
3802         * presume it needs http:// prepended (unless a relative
3803         * link starting with /, # or ? or a php file).
3804         */
3805        if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
3806                ! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
3807                $url = 'http://' . $url;
3808
3809        // Replace ampersands and single quotes only when displaying.
3810        if ( 'display' == $_context ) {
3811                $url = wp_kses_normalize_entities( $url );
3812                $url = str_replace( '&amp;', '&#038;', $url );
3813                $url = str_replace( "'", '&#039;', $url );
3814        }
3815
3816        if ( ( false !== strpos( $url, '[' ) ) || ( false !== strpos( $url, ']' ) ) ) {
3817
3818                $parsed = wp_parse_url( $url );
3819                $front  = '';
3820
3821                if ( isset( $parsed['scheme'] ) ) {
3822                        $front .= $parsed['scheme'] . '://';
3823                } elseif ( '/' === $url[0] ) {
3824                        $front .= '//';
3825                }
3826
3827                if ( isset( $parsed['user'] ) ) {
3828                        $front .= $parsed['user'];
3829                }
3830
3831                if ( isset( $parsed['pass'] ) ) {
3832                        $front .= ':' . $parsed['pass'];
3833                }
3834
3835                if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) {
3836                        $front .= '@';
3837                }
3838
3839                if ( isset( $parsed['host'] ) ) {
3840                        $front .= $parsed['host'];
3841                }
3842
3843                if ( isset( $parsed['port'] ) ) {
3844                        $front .= ':' . $parsed['port'];
3845                }
3846
3847                $end_dirty = str_replace( $front, '', $url );
3848                $end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty );
3849                $url       = str_replace( $end_dirty, $end_clean, $url );
3850
3851        }
3852
3853        if ( '/' === $url[0] ) {
3854                $good_protocol_url = $url;
3855        } else {
3856                if ( ! is_array( $protocols ) )
3857                        $protocols = wp_allowed_protocols();
3858                $good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
3859                if ( strtolower( $good_protocol_url ) != strtolower( $url ) )
3860                        return '';
3861        }
3862
3863        /**
3864         * Filters a string cleaned and escaped for output as a URL.
3865         *
3866         * @since 2.3.0
3867         *
3868         * @param string $good_protocol_url The cleaned URL to be returned.
3869         * @param string $original_url      The URL prior to cleaning.
3870         * @param string $_context          If 'display', replace ampersands and single quotes only.
3871         */
3872        return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context );
3873}
3874
3875/**
3876 * Performs esc_url() for database usage.
3877 *
3878 * @since 2.8.0
3879 *
3880 * @param string $url       The URL to be cleaned.
3881 * @param array  $protocols An array of acceptable protocols.
3882 * @return string The cleaned URL.
3883 */
3884function esc_url_raw( $url, $protocols = null ) {
3885        return esc_url( $url, $protocols, 'db' );
3886}
3887
3888/**
3889 * Convert entities, while preserving already-encoded entities.
3890 *
3891 * @link https://secure.php.net/htmlentities Borrowed from the PHP Manual user notes.
3892 *
3893 * @since 1.2.2
3894 *
3895 * @param string $myHTML The text to be converted.
3896 * @return string Converted text.
3897 */
3898function htmlentities2( $myHTML ) {
3899        $translation_table = get_html_translation_table( HTML_ENTITIES, ENT_QUOTES );
3900        $translation_table[chr(38)] = '&';
3901        return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,3};)/", "&amp;", strtr($myHTML, $translation_table) );
3902}
3903
3904/**
3905 * Escape single quotes, htmlspecialchar " < > &, and fix line endings.
3906 *
3907 * Escapes text strings for echoing in JS. It is intended to be used for inline JS
3908 * (in a tag attribute, for example onclick="..."). Note that the strings have to
3909 * be in single quotes. The {@see 'js_escape'} filter is also applied here.
3910 *
3911 * @since 2.8.0
3912 *
3913 * @param string $text The text to be escaped.
3914 * @return string Escaped text.
3915 */
3916function esc_js( $text ) {
3917        $safe_text = wp_check_invalid_utf8( $text );
3918        $safe_text = _wp_specialchars( $safe_text, ENT_COMPAT );
3919        $safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) );
3920        $safe_text = str_replace( "\r", '', $safe_text );
3921        $safe_text = str_replace( "\n", '\\n', addslashes( $safe_text ) );
3922        /**
3923         * Filters a string cleaned and escaped for output in JavaScript.
3924         *
3925         * Text passed to esc_js() is stripped of invalid or special characters,
3926         * and properly slashed for output.
3927         *
3928         * @since 2.0.6
3929         *
3930         * @param string $safe_text The text after it has been escaped.
3931         * @param string $text      The text prior to being escaped.
3932         */
3933        return apply_filters( 'js_escape', $safe_text, $text );
3934}
3935
3936/**
3937 * Escaping for HTML blocks.
3938 *
3939 * @since 2.8.0
3940 *
3941 * @param string $text
3942 * @return string
3943 */
3944function esc_html( $text ) {
3945        $safe_text = wp_check_invalid_utf8( $text );
3946        $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
3947        /**
3948         * Filters a string cleaned and escaped for output in HTML.
3949         *
3950         * Text passed to esc_html() is stripped of invalid or special characters
3951         * before output.
3952         *
3953         * @since 2.8.0
3954         *
3955         * @param string $safe_text The text after it has been escaped.
3956         * @param string $text      The text prior to being escaped.
3957         */
3958        return apply_filters( 'esc_html', $safe_text, $text );
3959}
3960
3961/**
3962 * Escaping for HTML attributes.
3963 *
3964 * @since 2.8.0
3965 *
3966 * @param string $text
3967 * @return string
3968 */
3969function esc_attr( $text ) {
3970        $safe_text = wp_check_invalid_utf8( $text );
3971        $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES );
3972        /**
3973         * Filters a string cleaned and escaped for output in an HTML attribute.
3974         *
3975         * Text passed to esc_attr() is stripped of invalid or special characters
3976         * before output.
3977         *
3978         * @since 2.0.6
3979         *
3980         * @param string $safe_text The text after it has been escaped.
3981         * @param string $text      The text prior to being escaped.
3982         */
3983        return apply_filters( 'attribute_escape', $safe_text, $text );
3984}
3985
3986/**
3987 * Escaping for textarea values.
3988 *
3989 * @since 3.1.0
3990 *
3991 * @param string $text
3992 * @return string
3993 */
3994function esc_textarea( $text ) {
3995        $safe_text = htmlspecialchars( $text, ENT_QUOTES, get_option( 'blog_charset' ) );
3996        /**
3997         * Filters a string cleaned and escaped for output in a textarea element.
3998         *
3999         * @since 3.1.0
4000         *
4001         * @param string $safe_text The text after it has been escaped.
4002         * @param string $text      The text prior to being escaped.
4003         */
4004        return apply_filters( 'esc_textarea', $safe_text, $text );
4005}
4006
4007/**
4008 * Escape an HTML tag name.
4009 *
4010 * @since 2.5.0
4011 *
4012 * @param string $tag_name
4013 * @return string
4014 */
4015function tag_escape( $tag_name ) {
4016        $safe_tag = strtolower( preg_replace('/[^a-zA-Z0-9_:]/', '', $tag_name) );
4017        /**
4018         * Filters a string cleaned and escaped for output as an HTML tag.
4019         *
4020         * @since 2.8.0
4021         *
4022         * @param string $safe_tag The tag name after it has been escaped.
4023         * @param string $tag_name The text before it was escaped.
4024         */
4025        return apply_filters( 'tag_escape', $safe_tag, $tag_name );
4026}
4027
4028/**
4029 * Convert full URL paths to absolute paths.
4030 *
4031 * Removes the http or https protocols and the domain. Keeps the path '/' at the
4032 * beginning, so it isn't a true relative link, but from the web root base.
4033 *
4034 * @since 2.1.0
4035 * @since 4.1.0 Support was added for relative URLs.
4036 *
4037 * @param string $link Full URL path.
4038 * @return string Absolute path.
4039 */
4040function wp_make_link_relative( $link ) {
4041        return preg_replace( '|^(https?:)?//[^/]+(/?.*)|i', '$2', $link );
4042}
4043
4044/**
4045 * Sanitises various option values based on the nature of the option.
4046 *
4047 * This is basically a switch statement which will pass $value through a number
4048 * of functions depending on the $option.
4049 *
4050 * @since 2.0.5
4051 *
4052 * @global wpdb $wpdb WordPress database abstraction object.
4053 *
4054 * @param string $option The name of the option.
4055 * @param string $value  The unsanitised value.
4056 * @return string Sanitized value.
4057 */
4058function sanitize_option( $option, $value ) {
4059        global $wpdb;
4060
4061        $original_value = $value;
4062        $error = '';
4063
4064        switch ( $option ) {
4065                case 'admin_email' :
4066                case 'new_admin_email' :
4067                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4068                        if ( is_wp_error( $value ) ) {
4069                                $error = $value->get_error_message();
4070                        } else {
4071                                $value = sanitize_email( $value );
4072                                if ( ! is_email( $value ) ) {
4073                                        $error = __( 'The email address entered did not appear to be a valid email address. Please enter a valid email address.' );
4074                                }
4075                        }
4076                        break;
4077
4078                case 'thumbnail_size_w':
4079                case 'thumbnail_size_h':
4080                case 'medium_size_w':
4081                case 'medium_size_h':
4082                case 'medium_large_size_w':
4083                case 'medium_large_size_h':
4084                case 'large_size_w':
4085                case 'large_size_h':
4086                case 'mailserver_port':
4087                case 'comment_max_links':
4088                case 'page_on_front':
4089                case 'page_for_posts':
4090                case 'rss_excerpt_length':
4091                case 'default_category':
4092                case 'default_email_category':
4093                case 'default_link_category':
4094                case 'close_comments_days_old':
4095                case 'comments_per_page':
4096                case 'thread_comments_depth':
4097                case 'users_can_register':
4098                case 'start_of_week':
4099                case 'site_icon':
4100                        $value = absint( $value );
4101                        break;
4102
4103                case 'posts_per_page':
4104                case 'posts_per_rss':
4105                        $value = (int) $value;
4106                        if ( empty($value) )
4107                                $value = 1;
4108                        if ( $value < -1 )
4109                                $value = abs($value);
4110                        break;
4111
4112                case 'default_ping_status':
4113                case 'default_comment_status':
4114                        // Options that if not there have 0 value but need to be something like "closed"
4115                        if ( $value == '0' || $value == '')
4116                                $value = 'closed';
4117                        break;
4118
4119                case 'blogdescription':
4120                case 'blogname':
4121                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4122                        if ( $value !== $original_value ) {
4123                                $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', wp_encode_emoji( $original_value ) );
4124                        }
4125
4126                        if ( is_wp_error( $value ) ) {
4127                                $error = $value->get_error_message();
4128                        } else {
4129                                $value = esc_html( $value );
4130                        }
4131                        break;
4132
4133                case 'blog_charset':
4134                        $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value); // strips slashes
4135                        break;
4136
4137                case 'blog_public':
4138                        // This is the value if the settings checkbox is not checked on POST. Don't rely on this.
4139                        if ( null === $value )
4140                                $value = 1;
4141                        else
4142                                $value = intval( $value );
4143                        break;
4144
4145                case 'date_format':
4146                case 'time_format':
4147                case 'mailserver_url':
4148                case 'mailserver_login':
4149                case 'mailserver_pass':
4150                case 'upload_path':
4151                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4152                        if ( is_wp_error( $value ) ) {
4153                                $error = $value->get_error_message();
4154                        } else {
4155                                $value = strip_tags( $value );
4156                                $value = wp_kses_data( $value );
4157                        }
4158                        break;
4159
4160                case 'ping_sites':
4161                        $value = explode( "\n", $value );
4162                        $value = array_filter( array_map( 'trim', $value ) );
4163                        $value = array_filter( array_map( 'esc_url_raw', $value ) );
4164                        $value = implode( "\n", $value );
4165                        break;
4166
4167                case 'gmt_offset':
4168                        $value = preg_replace('/[^0-9:.-]/', '', $value); // strips slashes
4169                        break;
4170
4171                case 'siteurl':
4172                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4173                        if ( is_wp_error( $value ) ) {
4174                                $error = $value->get_error_message();
4175                        } else {
4176                                if ( preg_match( '#http(s?)://(.+)#i', $value ) ) {
4177                                        $value = esc_url_raw( $value );
4178                                } else {
4179                                        $error = __( 'The WordPress address you entered did not appear to be a valid URL. Please enter a valid URL.' );
4180                                }
4181                        }
4182                        break;
4183
4184                case 'home':
4185                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4186                        if ( is_wp_error( $value ) ) {
4187                                $error = $value->get_error_message();
4188                        } else {
4189                                if ( preg_match( '#http(s?)://(.+)#i', $value ) ) {
4190                                        $value = esc_url_raw( $value );
4191                                } else {
4192                                        $error = __( 'The Site address you entered did not appear to be a valid URL. Please enter a valid URL.' );
4193                                }
4194                        }
4195                        break;
4196
4197                case 'WPLANG':
4198                        $allowed = get_available_languages();
4199                        if ( ! is_multisite() && defined( 'WPLANG' ) && '' !== WPLANG && 'en_US' !== WPLANG ) {
4200                                $allowed[] = WPLANG;
4201                        }
4202                        if ( ! in_array( $value, $allowed ) && ! empty( $value ) ) {
4203                                $value = get_option( $option );
4204                        }
4205                        break;
4206
4207                case 'illegal_names':
4208                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4209                        if ( is_wp_error( $value ) ) {
4210                                $error = $value->get_error_message();
4211                        } else {
4212                                if ( ! is_array( $value ) )
4213                                        $value = explode( ' ', $value );
4214
4215                                $value = array_values( array_filter( array_map( 'trim', $value ) ) );
4216
4217                                if ( ! $value )
4218                                        $value = '';
4219                        }
4220                        break;
4221
4222                case 'limited_email_domains':
4223                case 'banned_email_domains':
4224                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4225                        if ( is_wp_error( $value ) ) {
4226                                $error = $value->get_error_message();
4227                        } else {
4228                                if ( ! is_array( $value ) )
4229                                        $value = explode( "\n", $value );
4230
4231                                $domains = array_values( array_filter( array_map( 'trim', $value ) ) );
4232                                $value = array();
4233
4234                                foreach ( $domains as $domain ) {
4235                                        if ( ! preg_match( '/(--|\.\.)/', $domain ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $domain ) ) {
4236                                                $value[] = $domain;
4237                                        }
4238                                }
4239                                if ( ! $value )
4240                                        $value = '';
4241                        }
4242                        break;
4243
4244                case 'timezone_string':
4245                        $allowed_zones = timezone_identifiers_list();
4246                        if ( ! in_array( $value, $allowed_zones ) && ! empty( $value ) ) {
4247                                $error = __( 'The timezone you have entered is not valid. Please select a valid timezone.' );
4248                        }
4249                        break;
4250
4251                case 'permalink_structure':
4252                case 'category_base':
4253                case 'tag_base':
4254                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4255                        if ( is_wp_error( $value ) ) {
4256                                $error = $value->get_error_message();
4257                        } else {
4258                                $value = esc_url_raw( $value );
4259                                $value = str_replace( 'http://', '', $value );
4260                        }
4261
4262                        if ( 'permalink_structure' === $option && '' !== $value && ! preg_match( '/%[^\/%]+%/', $value ) ) {
4263                                $error = sprintf(
4264                                        /* translators: %s: Codex URL */
4265                                        __( 'A structure tag is required when using custom permalinks. <a href="%s">Learn more</a>' ),
4266                                        __( 'https://codex.wordpress.org/Using_Permalinks#Choosing_your_permalink_structure' )
4267                                );
4268                        }
4269                        break;
4270
4271                case 'default_role' :
4272                        if ( ! get_role( $value ) && get_role( 'subscriber' ) )
4273                                $value = 'subscriber';
4274                        break;
4275
4276                case 'moderation_keys':
4277                case 'blacklist_keys':
4278                        $value = $wpdb->strip_invalid_text_for_column( $wpdb->options, 'option_value', $value );
4279                        if ( is_wp_error( $value ) ) {
4280                                $error = $value->get_error_message();
4281                        } else {
4282                                $value = explode( "\n", $value );
4283                                $value = array_filter( array_map( 'trim', $value ) );
4284                                $value = array_unique( $value );
4285                                $value = implode( "\n", $value );
4286                        }
4287                        break;
4288        }
4289
4290        if ( ! empty( $error ) ) {
4291                $value = get_option( $option );
4292                if ( function_exists( 'add_settings_error' ) ) {
4293                        add_settings_error( $option, "invalid_{$option}", $error );
4294                }
4295        }
4296
4297        /**
4298         * Filters an option value following sanitization.
4299         *
4300         * @since 2.3.0
4301         * @since 4.3.0 Added the `$original_value` parameter.
4302         *
4303         * @param string $value          The sanitized option value.
4304         * @param string $option         The option name.
4305         * @param string $original_value The original value passed to the function.
4306         */
4307        return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value );
4308}
4309
4310/**
4311 * Maps a function to all non-iterable elements of an array or an object.
4312 *
4313 * This is similar to `array_walk_recursive()` but acts upon objects too.
4314 *
4315 * @since 4.4.0
4316 *
4317 * @param mixed    $value    The array, object, or scalar.
4318 * @param callable $callback The function to map onto $value.
4319 * @return mixed The value with the callback applied to all non-arrays and non-objects inside it.
4320 */
4321function map_deep( $value, $callback ) {
4322        if ( is_array( $value ) ) {
4323                foreach ( $value as $index => $item ) {
4324                        $value[ $index ] = map_deep( $item, $callback );
4325                }
4326        } elseif ( is_object( $value ) ) {
4327                $object_vars = get_object_vars( $value );
4328                foreach ( $object_vars as $property_name => $property_value ) {
4329                        $value->$property_name = map_deep( $property_value, $callback );
4330                }
4331        } else {
4332                $value = call_user_func( $callback, $value );
4333        }
4334
4335        return $value;
4336}
4337
4338/**
4339 * Parses a string into variables to be stored in an array.
4340 *
4341 * Uses {@link https://secure.php.net/parse_str parse_str()} and stripslashes if
4342 * {@link https://secure.php.net/magic_quotes magic_quotes_gpc} is on.
4343 *
4344 * @since 2.2.1
4345 *
4346 * @param string $string The string to be parsed.
4347 * @param array  $array  Variables will be stored in this array.
4348 */
4349function wp_parse_str( $string, &$array ) {
4350        parse_str( $string, $array );
4351        if ( get_magic_quotes_gpc() )
4352                $array = stripslashes_deep( $array );
4353        /**
4354         * Filters the array of variables derived from a parsed string.
4355         *
4356         * @since 2.3.0
4357         *
4358         * @param array $array The array populated with variables.
4359         */
4360        $array = apply_filters( 'wp_parse_str', $array );
4361}
4362
4363/**
4364 * Convert lone less than signs.
4365 *
4366 * KSES already converts lone greater than signs.
4367 *
4368 * @since 2.3.0
4369 *
4370 * @param string $text Text to be converted.
4371 * @return string Converted text.
4372 */
4373function wp_pre_kses_less_than( $text ) {
4374        return preg_replace_callback('%<[^>]*?((?=<)|>|$)%', 'wp_pre_kses_less_than_callback', $text);
4375}
4376
4377/**
4378 * Callback function used by preg_replace.
4379 *
4380 * @since 2.3.0
4381 *
4382 * @param array $matches Populated by matches to preg_replace.
4383 * @return string The text returned after esc_html if needed.
4384 */
4385function wp_pre_kses_less_than_callback( $matches ) {
4386        if ( false === strpos($matches[0], '>') )
4387                return esc_html($matches[0]);
4388        return $matches[0];
4389}
4390
4391/**
4392 * WordPress implementation of PHP sprintf() with filters.
4393 *
4394 * @since 2.5.0
4395 * @link https://secure.php.net/sprintf
4396 *
4397 * @param string $pattern   The string which formatted args are inserted.
4398 * @param mixed  $args ,... Arguments to be formatted into the $pattern string.
4399 * @return string The formatted string.
4400 */
4401function wp_sprintf( $pattern ) {
4402        $args = func_get_args();
4403        $len = strlen($pattern);
4404        $start = 0;
4405        $result = '';
4406        $arg_index = 0;
4407        while ( $len > $start ) {
4408                // Last character: append and break
4409                if ( strlen($pattern) - 1 == $start ) {
4410                        $result .= substr($pattern, -1);
4411                        break;
4412                }
4413
4414                // Literal %: append and continue
4415                if ( substr($pattern, $start, 2) == '%%' ) {
4416                        $start += 2;
4417                        $result .= '%';
4418                        continue;
4419                }
4420
4421                // Get fragment before next %
4422                $end = strpos($pattern, '%', $start + 1);
4423                if ( false === $end )
4424                        $end = $len;
4425                $fragment = substr($pattern, $start, $end - $start);
4426
4427                // Fragment has a specifier
4428                if ( $pattern[$start] == '%' ) {
4429                        // Find numbered arguments or take the next one in order
4430                        if ( preg_match('/^%(\d+)\$/', $fragment, $matches) ) {
4431                                $arg = isset($args[$matches[1]]) ? $args[$matches[1]] : '';
4432                                $fragment = str_replace("%{$matches[1]}$", '%', $fragment);
4433                        } else {
4434                                ++$arg_index;
4435                                $arg = isset($args[$arg_index]) ? $args[$arg_index] : '';
4436                        }
4437
4438                        /**
4439                         * Filters a fragment from the pattern passed to wp_sprintf().
4440                         *
4441                         * If the fragment is unchanged, then sprintf() will be run on the fragment.
4442                         *
4443                         * @since 2.5.0
4444                         *
4445                         * @param string $fragment A fragment from the pattern.
4446                         * @param string $arg      The argument.
4447                         */
4448                        $_fragment = apply_filters( 'wp_sprintf', $fragment, $arg );
4449                        if ( $_fragment != $fragment )
4450                                $fragment = $_fragment;
4451                        else
4452                                $fragment = sprintf($fragment, strval($arg) );
4453                }
4454
4455                // Append to result and move to next fragment
4456                $result .= $fragment;
4457                $start = $end;
4458        }
4459        return $result;
4460}
4461
4462/**
4463 * Localize list items before the rest of the content.
4464 *
4465 * The '%l' must be at the first characters can then contain the rest of the
4466 * content. The list items will have ', ', ', and', and ' and ' added depending
4467 * on the amount of list items in the $args parameter.
4468 *
4469 * @since 2.5.0
4470 *
4471 * @param string $pattern Content containing '%l' at the beginning.
4472 * @param array  $args    List items to prepend to the content and replace '%l'.
4473 * @return string Localized list items and rest of the content.
4474 */
4475function wp_sprintf_l( $pattern, $args ) {
4476        // Not a match
4477        if ( substr($pattern, 0, 2) != '%l' )
4478                return $pattern;
4479
4480        // Nothing to work with
4481        if ( empty($args) )
4482                return '';
4483
4484        /**
4485         * Filters the translated delimiters used by wp_sprintf_l().
4486         * Placeholders (%s) are included to assist translators and then
4487         * removed before the array of strings reaches the filter.
4488         *
4489         * Please note: Ampersands and entities should be avoided here.
4490         *
4491         * @since 2.5.0
4492         *
4493         * @param array $delimiters An array of translated delimiters.
4494         */
4495        $l = apply_filters( 'wp_sprintf_l', array(
4496                /* translators: used to join items in a list with more than 2 items */
4497                'between'          => sprintf( __('%s, %s'), '', '' ),
4498                /* translators: used to join last two items in a list with more than 2 times */
4499                'between_last_two' => sprintf( __('%s, and %s'), '', '' ),
4500                /* translators: used to join items in a list with only 2 items */
4501                'between_only_two' => sprintf( __('%s and %s'), '', '' ),
4502        ) );
4503
4504        $args = (array) $args;
4505        $result = array_shift($args);
4506        if ( count($args) == 1 )
4507                $result .= $l['between_only_two'] . array_shift($args);
4508        // Loop when more than two args
4509        $i = count($args);
4510        while ( $i ) {
4511                $arg = array_shift($args);
4512                $i--;
4513                if ( 0 == $i )
4514                        $result .= $l['between_last_two'] . $arg;
4515                else
4516                        $result .= $l['between'] . $arg;
4517        }
4518        return $result . substr($pattern, 2);
4519}
4520
4521/**
4522 * Safely extracts not more than the first $count characters from html string.
4523 *
4524 * UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
4525 * be counted as one character. For example &amp; will be counted as 4, &lt; as
4526 * 3, etc.
4527 *
4528 * @since 2.5.0
4529 *
4530 * @param string $str   String to get the excerpt from.
4531 * @param int    $count Maximum number of characters to take.
4532 * @param string $more  Optional. What to append if $str needs to be trimmed. Defaults to empty string.
4533 * @return string The excerpt.
4534 */
4535function wp_html_excerpt( $str, $count, $more = null ) {
4536        if ( null === $more )
4537                $more = '';
4538        $str = wp_strip_all_tags( $str, true );
4539        $excerpt = mb_substr( $str, 0, $count );
4540        // remove part of an entity at the end
4541        $excerpt = preg_replace( '/&[^;\s]{0,6}$/', '', $excerpt );
4542        if ( $str != $excerpt )
4543                $excerpt = trim( $excerpt ) . $more;
4544        return $excerpt;
4545}
4546
4547/**
4548 * Add a Base url to relative links in passed content.
4549 *
4550 * By default it supports the 'src' and 'href' attributes. However this can be
4551 * changed via the 3rd param.
4552 *
4553 * @since 2.7.0
4554 *
4555 * @global string $_links_add_base
4556 *
4557 * @param string $content String to search for links in.
4558 * @param string $base    The base URL to prefix to links.
4559 * @param array  $attrs   The attributes which should be processed.
4560 * @return string The processed content.
4561 */
4562function links_add_base_url( $content, $base, $attrs = array('src', 'href') ) {
4563        global $_links_add_base;
4564        $_links_add_base = $base;
4565        $attrs = implode('|', (array)$attrs);
4566        return preg_replace_callback( "!($attrs)=(['\"])(.+?)\\2!i", '_links_add_base', $content );
4567}
4568
4569/**
4570 * Callback to add a base url to relative links in passed content.
4571 *
4572 * @since 2.7.0
4573 * @access private
4574 *
4575 * @global string $_links_add_base
4576 *
4577 * @param string $m The matched link.
4578 * @return string The processed link.
4579 */
4580function _links_add_base( $m ) {
4581        global $_links_add_base;
4582        //1 = attribute name  2 = quotation mark  3 = URL
4583        return $m[1] . '=' . $m[2] .
4584                ( preg_match( '#^(\w{1,20}):#', $m[3], $protocol ) && in_array( $protocol[1], wp_allowed_protocols() ) ?
4585                        $m[3] :
4586                        WP_Http::make_absolute_url( $m[3], $_links_add_base )
4587                )
4588                . $m[2];
4589}
4590
4591/**
4592 * Adds a Target attribute to all links in passed content.
4593 *
4594 * This function by default only applies to `<a>` tags, however this can be
4595 * modified by the 3rd param.
4596 *
4597 * *NOTE:* Any current target attributed will be stripped and replaced.
4598 *
4599 * @since 2.7.0
4600 *
4601 * @global string $_links_add_target
4602 *
4603 * @param string $content String to search for links in.
4604 * @param string $target  The Target to add to the links.
4605 * @param array  $tags    An array of tags to apply to.
4606 * @return string The processed content.
4607 */
4608function links_add_target( $content, $target = '_blank', $tags = array('a') ) {
4609        global $_links_add_target;
4610        $_links_add_target = $target;
4611        $tags = implode('|', (array)$tags);
4612        return preg_replace_callback( "!<($tags)([^>]*)>!i", '_links_add_target', $content );
4613}
4614
4615/**
4616 * Callback to add a target attribute to all links in passed content.
4617 *
4618 * @since 2.7.0
4619 * @access private
4620 *
4621 * @global string $_links_add_target
4622 *
4623 * @param string $m The matched link.
4624 * @return string The processed link.
4625 */
4626function _links_add_target( $m ) {
4627        global $_links_add_target;
4628        $tag = $m[1];
4629        $link = preg_replace('|( target=([\'"])(.*?)\2)|i', '', $m[2]);
4630        return '<' . $tag . $link . ' target="' . esc_attr( $_links_add_target ) . '">';
4631}
4632
4633/**
4634 * Normalize EOL characters and strip duplicate whitespace.
4635 *
4636 * @since 2.7.0
4637 *
4638 * @param string $str The string to normalize.
4639 * @return string The normalized string.
4640 */
4641function normalize_whitespace( $str ) {
4642        $str  = trim( $str );
4643        $str  = str_replace( "\r", "\n", $str );
4644        $str  = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str );
4645        return $str;
4646}
4647
4648/**
4649 * Properly strip all HTML tags including script and style
4650 *
4651 * This differs from strip_tags() because it removes the contents of
4652 * the `<script>` and `<style>` tags. E.g. `strip_tags( '<script>something</script>' )`
4653 * will return 'something'. wp_strip_all_tags will return ''
4654 *
4655 * @since 2.9.0
4656 *
4657 * @param string $string        String containing HTML tags
4658 * @param bool   $remove_breaks Optional. Whether to remove left over line breaks and white space chars
4659 * @return string The processed string.
4660 */
4661function wp_strip_all_tags($string, $remove_breaks = false) {
4662        $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
4663        $string = strip_tags($string);
4664
4665        if ( $remove_breaks )
4666                $string = preg_replace('/[\r\n\t ]+/', ' ', $string);
4667
4668        return trim( $string );
4669}
4670
4671/**
4672 * Sanitizes a string from user input or from the database.
4673 *
4674 * - Checks for invalid UTF-8,
4675 * - Converts single `<` characters to entities
4676 * - Strips all tags
4677 * - Removes line breaks, tabs, and extra whitespace
4678 * - Strips octets
4679 *
4680 * @since 2.9.0
4681 *
4682 * @see sanitize_textarea_field()
4683 * @see wp_check_invalid_utf8()
4684 * @see wp_strip_all_tags()
4685 *
4686 * @param string $str String to sanitize.
4687 * @return string Sanitized string.
4688 */
4689function sanitize_text_field( $str ) {
4690        $filtered = _sanitize_text_fields( $str, false );
4691
4692        /**
4693         * Filters a sanitized text field string.
4694         *
4695         * @since 2.9.0
4696         *
4697         * @param string $filtered The sanitized string.
4698         * @param string $str      The string prior to being sanitized.
4699         */
4700        return apply_filters( 'sanitize_text_field', $filtered, $str );
4701}
4702
4703/**
4704 * Sanitizes a multiline string from user input or from the database.
4705 *
4706 * The function is like sanitize_text_field(), but preserves
4707 * new lines (\n) and other whitespace, which are legitimate
4708 * input in textarea elements.
4709 *
4710 * @see sanitize_text_field()
4711 *
4712 * @since 4.7.0
4713 *
4714 * @param string $str String to sanitize.
4715 * @return string Sanitized string.
4716 */
4717function sanitize_textarea_field( $str ) {
4718        $filtered = _sanitize_text_fields( $str, true );
4719
4720        /**
4721         * Filters a sanitized textarea field string.
4722         *
4723         * @since 4.7.0
4724         *
4725         * @param string $filtered The sanitized string.
4726         * @param string $str      The string prior to being sanitized.
4727         */
4728        return apply_filters( 'sanitize_textarea_field', $filtered, $str );
4729}
4730
4731/**
4732 * Internal helper function to sanitize a string from user input or from the db
4733 *
4734 * @since 4.7.0
4735 * @access private
4736 *
4737 * @param string $str String to sanitize.
4738 * @param bool $keep_newlines optional Whether to keep newlines. Default: false.
4739 * @return string Sanitized string.
4740 */
4741function _sanitize_text_fields( $str, $keep_newlines = false ) {
4742        $filtered = wp_check_invalid_utf8( $str );
4743
4744        if ( strpos($filtered, '<') !== false ) {
4745                $filtered = wp_pre_kses_less_than( $filtered );
4746                // This will strip extra whitespace for us.
4747                $filtered = wp_strip_all_tags( $filtered, false );
4748
4749                // Use html entities in a special case to make sure no later
4750                // newline stripping stage could lead to a functional tag
4751                $filtered = str_replace("<\n", "&lt;\n", $filtered);
4752        }
4753
4754        if ( ! $keep_newlines ) {
4755                $filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
4756        }
4757        $filtered = trim( $filtered );
4758
4759        $found = false;
4760        while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) {
4761                $filtered = str_replace($match[0], '', $filtered);
4762                $found = true;
4763        }
4764
4765        if ( $found ) {
4766                // Strip out the whitespace that may now exist after removing the octets.
4767                $filtered = trim( preg_replace('/ +/', ' ', $filtered) );
4768        }
4769
4770        return $filtered;
4771}
4772
4773/**
4774 * i18n friendly version of basename()
4775 *
4776 * @since 3.1.0
4777 *
4778 * @param string $path   A path.
4779 * @param string $suffix If the filename ends in suffix this will also be cut off.
4780 * @return string
4781 */
4782function wp_basename( $path, $suffix = '' ) {
4783        return urldecode( basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) );
4784}
4785
4786/**
4787 * Forever eliminate "Wordpress" from the planet (or at least the little bit we can influence).
4788 *
4789 * Violating our coding standards for a good function name.
4790 *
4791 * @since 3.0.0
4792 *
4793 * @staticvar string|false $dblq
4794 *
4795 * @param string $text The text to be modified.
4796 * @return string The modified text.
4797 */
4798function capital_P_dangit( $text ) {
4799        // Simple replacement for titles
4800        $current_filter = current_filter();
4801        if ( 'the_title' === $current_filter || 'wp_title' === $current_filter )
4802                return str_replace( 'Wordpress', 'WordPress', $text );
4803        // Still here? Use the more judicious replacement
4804        static $dblq = false;
4805        if ( false === $dblq ) {
4806                $dblq = _x( '&#8220;', 'opening curly double quote' );
4807        }
4808        return str_replace(
4809                array( ' Wordpress', '&#8216;Wordpress', $dblq . 'Wordpress', '>Wordpress', '(Wordpress' ),
4810                array( ' WordPress', '&#8216;WordPress', $dblq . 'WordPress', '>WordPress', '(WordPress' ),
4811        $text );
4812}
4813
4814/**
4815 * Sanitize a mime type
4816 *
4817 * @since 3.1.3
4818 *
4819 * @param string $mime_type Mime type
4820 * @return string Sanitized mime type
4821 */
4822function sanitize_mime_type( $mime_type ) {
4823        $sani_mime_type = preg_replace( '/[^-+*.a-zA-Z0-9\/]/', '', $mime_type );
4824        /**
4825         * Filters a mime type following sanitization.
4826         *
4827         * @since 3.1.3
4828         *
4829         * @param string $sani_mime_type The sanitized mime type.
4830         * @param string $mime_type      The mime type prior to sanitization.
4831         */
4832        return apply_filters( 'sanitize_mime_type', $sani_mime_type, $mime_type );
4833}
4834
4835/**
4836 * Sanitize space or carriage return separated URLs that are used to send trackbacks.
4837 *
4838 * @since 3.4.0
4839 *
4840 * @param string $to_ping Space or carriage return separated URLs
4841 * @return string URLs starting with the http or https protocol, separated by a carriage return.
4842 */
4843function sanitize_trackback_urls( $to_ping ) {
4844        $urls_to_ping = preg_split( '/[\r\n\t ]/', trim( $to_ping ), -1, PREG_SPLIT_NO_EMPTY );
4845        foreach ( $urls_to_ping as $k => $url ) {
4846                if ( !preg_match( '#^https?://.#i', $url ) )
4847                        unset( $urls_to_ping[$k] );
4848        }
4849        $urls_to_ping = array_map( 'esc_url_raw', $urls_to_ping );
4850        $urls_to_ping = implode( "\n", $urls_to_ping );
4851        /**
4852         * Filters a list of trackback URLs following sanitization.
4853         *
4854         * The string returned here consists of a space or carriage return-delimited list
4855         * of trackback URLs.
4856         *
4857         * @since 3.4.0
4858         *
4859         * @param string $urls_to_ping Sanitized space or carriage return separated URLs.
4860         * @param string $to_ping      Space or carriage return separated URLs before sanitization.
4861         */
4862        return apply_filters( 'sanitize_trackback_urls', $urls_to_ping, $to_ping );
4863}
4864
4865/**
4866 * Add slashes to a string or array of strings.
4867 *
4868 * This should be used when preparing data for core API that expects slashed data.
4869 * This should not be used to escape data going directly into an SQL query.
4870 *
4871 * @since 3.6.0
4872 *
4873 * @param string|array $value String or array of strings to slash.
4874 * @return string|array Slashed $value
4875 */
4876function wp_slash( $value ) {
4877        if ( is_array( $value ) ) {
4878                foreach ( $value as $k => $v ) {
4879                        if ( is_array( $v ) ) {
4880                                $value[$k] = wp_slash( $v );
4881                        } else {
4882                                $value[$k] = addslashes( $v );
4883                        }
4884                }
4885        } else {
4886                $value = addslashes( $value );
4887        }
4888
4889        return $value;
4890}
4891
4892/**
4893 * Remove slashes from a string or array of strings.
4894 *
4895 * This should be used to remove slashes from data passed to core API that
4896 * expects data to be unslashed.
4897 *
4898 * @since 3.6.0
4899 *
4900 * @param string|array $value String or array of strings to unslash.
4901 * @return string|array Unslashed $value
4902 */
4903function wp_unslash( $value ) {
4904        return stripslashes_deep( $value );
4905}
4906
4907/**
4908 * Extract and return the first URL from passed content.
4909 *
4910 * @since 3.6.0
4911 *
4912 * @param string $content A string which might contain a URL.
4913 * @return string|false The found URL.
4914 */
4915function get_url_in_content( $content ) {
4916        if ( empty( $content ) ) {
4917                return false;
4918        }
4919
4920        if ( preg_match( '/<a\s[^>]*?href=([\'"])(.+?)\1/is', $content, $matches ) ) {
4921                return esc_url_raw( $matches[2] );
4922        }
4923
4924        return false;
4925}
4926
4927/**
4928 * Returns the regexp for common whitespace characters.
4929 *
4930 * By default, spaces include new lines, tabs, nbsp entities, and the UTF-8 nbsp.
4931 * This is designed to replace the PCRE \s sequence.  In ticket #22692, that
4932 * sequence was found to be unreliable due to random inclusion of the A0 byte.
4933 *
4934 * @since 4.0.0
4935 *
4936 * @staticvar string $spaces
4937 *
4938 * @return string The spaces regexp.
4939 */
4940function wp_spaces_regexp() {
4941        static $spaces = '';
4942
4943        if ( empty( $spaces ) ) {
4944                /**
4945                 * Filters the regexp for common whitespace characters.
4946                 *
4947                 * This string is substituted for the \s sequence as needed in regular
4948                 * expressions. For websites not written in English, different characters
4949                 * may represent whitespace. For websites not encoded in UTF-8, the 0xC2 0xA0
4950                 * sequence may not be in use.
4951                 *
4952                 * @since 4.0.0
4953                 *
4954                 * @param string $spaces Regexp pattern for matching common whitespace characters.
4955                 */
4956                $spaces = apply_filters( 'wp_spaces_regexp', '[\r\n\t ]|\xC2\xA0|&nbsp;' );
4957        }
4958
4959        return $spaces;
4960}
4961
4962/**
4963 * Print the important emoji-related styles.
4964 *
4965 * @since 4.2.0
4966 *
4967 * @staticvar bool $printed
4968 */
4969function print_emoji_styles() {
4970        static $printed = false;
4971
4972        if ( $printed ) {
4973                return;
4974        }
4975
4976        $printed = true;
4977?>
4978<style type="text/css">
4979img.wp-smiley,
4980img.emoji {
4981        display: inline !important;
4982        border: none !important;
4983        box-shadow: none !important;
4984        height: 1em !important;
4985        width: 1em !important;
4986        margin: 0 .07em !important;
4987        vertical-align: -0.1em !important;
4988        background: none !important;
4989        padding: 0 !important;
4990}
4991</style>
4992<?php
4993}
4994
4995/**
4996 * Print the inline Emoji detection script if it is not already printed.
4997 *
4998 * @since 4.2.0
4999 * @staticvar bool $printed
5000 */
5001function print_emoji_detection_script() {
5002        static $printed = false;
5003
5004        if ( $printed ) {
5005                return;
5006        }
5007
5008        $printed = true;
5009
5010        _print_emoji_detection_script();
5011}
5012
5013/**
5014 * Prints inline Emoji dection script
5015 *
5016 * @ignore
5017 * @since 4.6.0
5018 * @access private
5019 */
5020function _print_emoji_detection_script() {
5021        $settings = array(
5022                /**
5023                 * Filters the URL where emoji png images are hosted.
5024                 *
5025                 * @since 4.2.0
5026                 *
5027                 * @param string The emoji base URL for png images.
5028                 */
5029                'baseUrl' => apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/2.3/72x72/' ),
5030
5031                /**
5032                 * Filters the extension of the emoji png files.
5033                 *
5034                 * @since 4.2.0
5035                 *
5036                 * @param string The emoji extension for png files. Default .png.
5037                 */
5038                'ext' => apply_filters( 'emoji_ext', '.png' ),
5039
5040                /**
5041                 * Filters the URL where emoji SVG images are hosted.
5042                 *
5043                 * @since 4.6.0
5044                 *
5045                 * @param string The emoji base URL for svg images.
5046                 */
5047                'svgUrl' => apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/2.3/svg/' ),
5048
5049                /**
5050                 * Filters the extension of the emoji SVG files.
5051                 *
5052                 * @since 4.6.0
5053                 *
5054                 * @param string The emoji extension for svg files. Default .svg.
5055                 */
5056                'svgExt' => apply_filters( 'emoji_svg_ext', '.svg' ),
5057        );
5058
5059        $version = 'ver=' . get_bloginfo( 'version' );
5060
5061        if ( SCRIPT_DEBUG ) {
5062                $settings['source'] = array(
5063                        /** This filter is documented in wp-includes/class.wp-scripts.php */
5064                        'wpemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji.js?$version" ), 'wpemoji' ),
5065                        /** This filter is documented in wp-includes/class.wp-scripts.php */
5066                        'twemoji' => apply_filters( 'script_loader_src', includes_url( "js/twemoji.js?$version" ), 'twemoji' ),
5067                );
5068
5069                ?>
5070                <script type="text/javascript">
5071                        window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
5072                        <?php readfile( ABSPATH . WPINC . "/js/wp-emoji-loader.js" ); ?>
5073                </script>
5074                <?php
5075        } else {
5076                $settings['source'] = array(
5077                        /** This filter is documented in wp-includes/class.wp-scripts.php */
5078                        'concatemoji' => apply_filters( 'script_loader_src', includes_url( "js/wp-emoji-release.min.js?$version" ), 'concatemoji' ),
5079                );
5080
5081                /*
5082                 * If you're looking at a src version of this file, you'll see an "include"
5083                 * statement below. This is used by the `grunt build` process to directly
5084                 * include a minified version of wp-emoji-loader.js, instead of using the
5085                 * readfile() method from above.
5086                 *
5087                 * If you're looking at a build version of this file, you'll see a string of
5088                 * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
5089                 * and edit wp-emoji-loader.js directly.
5090                 */
5091                ?>
5092                <script type="text/javascript">
5093                        window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
5094                        !function(a,b,c){function d(a){var b,c,d,e,f=String.fromCharCode;if(!k||!k.fillText)return!1;switch(k.clearRect(0,0,j.width,j.height),k.textBaseline="top",k.font="600 32px Arial",a){case"flag":return k.fillText(f(55356,56826,55356,56819),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,56826,8203,55356,56819),0,0),c=j.toDataURL(),b===c&&(k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447),0,0),c=j.toDataURL(),b!==c);case"emoji4":return k.fillText(f(55358,56794,8205,9794,65039),0,0),d=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55358,56794,8203,9794,65039),0,0),e=j.toDataURL(),d!==e}return!1}function e(a){var c=b.createElement("script");c.src=a,c.defer=c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i,j=b.createElement("canvas"),k=j.getContext&&j.getContext("2d");for(i=Array("flag","emoji4"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
5095                </script>
5096                <?php
5097        }
5098}
5099
5100/**
5101 * Convert emoji characters to their equivalent HTML entity.
5102 *
5103 * This allows us to store emoji in a DB using the utf8 character set.
5104 *
5105 * @since 4.2.0
5106 *
5107 * @param string $content The content to encode.
5108 * @return string The encoded content.
5109 */
5110function wp_encode_emoji( $content ) {
5111        if ( function_exists( 'mb_convert_encoding' ) ) {
5112                $regex = wp_emoji_regex( 'codepoints' );
5113
5114                $matches = array();
5115                if ( preg_match_all( $regex, $content, $matches ) ) {
5116                        if ( ! empty( $matches[1] ) ) {
5117                                foreach ( array_unique( $matches[1] ) as $emoji ) {
5118                                        /*
5119                                         * UTF-32's hex encoding is the same as HTML's hex encoding.
5120                                         * So, by converting the emoji from UTF-8 to UTF-32, we magically
5121                                         * get the correct hex encoding, 0 padded to 8 characters.
5122                                         */
5123                                        $unpacked = unpack( 'H*', mb_convert_encoding( $emoji, 'UTF-32', 'UTF-8' ) );
5124                                        if ( isset( $unpacked[1] ) ) {
5125                                                $parts = str_split( $unpacked[1], 8 );
5126                                                $entity = '';
5127
5128                                                foreach ( $parts as $part ) {
5129                                                        $entity .= '&#x' . ltrim( $part, '0' ) . ';';
5130                                                }
5131
5132                                                $content = str_replace( $emoji, $entity, $content );
5133                                        }
5134                                }
5135                        }
5136                }
5137        }
5138
5139        return $content;
5140}
5141
5142/**
5143 * Convert emoji to a static img element.
5144 *
5145 * @since 4.2.0
5146 *
5147 * @param string $text The content to encode.
5148 * @return string The encoded content.
5149 */
5150function wp_staticize_emoji( $text ) {
5151        $text = wp_encode_emoji( $text );
5152
5153        $output = '';
5154        /*
5155         * HTML loop taken from smiley function, which was taken from texturize function.
5156         * It'll never be consolidated.
5157         *
5158         * First, capture the tags as well as in between.
5159         */
5160        $textarr = preg_split( '/(<.*>)/U', $text, -1, PREG_SPLIT_DELIM_CAPTURE );
5161        $stop = count( $textarr );
5162
5163        // Ignore processing of specific tags.
5164        $tags_to_ignore = 'code|pre|style|script|textarea';
5165        $ignore_block_element = '';
5166
5167        $regex = wp_emoji_regex( 'entities' );
5168
5169        for ( $i = 0; $i < $stop; $i++ ) {
5170                $content = $textarr[$i];
5171
5172                // If we're in an ignore block, wait until we find its closing tag.
5173                if ( '' == $ignore_block_element && preg_match( '/^<(' . $tags_to_ignore . ')>/', $content, $matches ) )  {
5174                        $ignore_block_element = $matches[1];
5175                }
5176
5177                // If it's not a tag and not in ignore block.
5178                if ( '' ==  $ignore_block_element && strlen( $content ) > 0 && '<' != $content[0] ) {
5179                        $content = preg_replace_callback( $regex, '_wp_staticize_emoji', $content );
5180                }
5181
5182                // Did we exit ignore block.
5183                if ( '' != $ignore_block_element && '</' . $ignore_block_element . '>' == $content )  {
5184                        $ignore_block_element = '';
5185                }
5186
5187                $output .= $content;
5188        }
5189
5190        return $output;
5191}
5192
5193/**
5194 * Callback for wp_staticize_emoji() to turn matched emoji glyphs into images.
5195 *
5196 * @since 4.8.1
5197 * @access private
5198 *
5199 * @see wp_staticize_emoji()
5200 * @staticvar string $cdn_url The CDN url returned by the {@see 'emoji_url'} filter.
5201 * @staticvar string $ext     The file extension returned by the {@see 'emoji_ext'} filter.
5202 *
5203 * @param  array $matches The matched data.
5204 * @return string HTML for the static emoji image.
5205 */
5206function _wp_staticize_emoji( $matches ) {
5207        static $cdn_url;
5208        if ( ! $cdn_url ) {
5209                /** This filter is documented in wp-includes/formatting.php */
5210                $cdn_url = apply_filters( 'emoji_url', 'https://s.w.org/images/core/emoji/2.3/72x72/' );
5211        }
5212
5213        static $ext;
5214        if ( ! $ext ) {
5215                /** This filter is documented in wp-includes/formatting.php */
5216                $ext = apply_filters( 'emoji_ext', '.png' );
5217        }
5218
5219        $char = str_replace( ';&#x', '-', $matches[1] );
5220        $char = str_replace( array( '&#x', ';'), '', $char );
5221
5222        return sprintf( '<img src="%s" alt="%s" class="wp-smiley" style="height: 1em; max-height: 1em;" />', $cdn_url . $char . $ext, html_entity_decode( $matches[1] ) );
5223}
5224
5225/**
5226 * Convert emoji in emails into static images.
5227 *
5228 * @since 4.2.0
5229 *
5230 * @param array $mail The email data array.
5231 * @return array The email data array, with emoji in the message staticized.
5232 */
5233function wp_staticize_emoji_for_email( $mail ) {
5234        if ( ! isset( $mail['message'] ) ) {
5235                return $mail;
5236        }
5237
5238        /*
5239         * We can only transform the emoji into images if it's a text/html email.
5240         * To do that, here's a cut down version of the same process that happens
5241         * in wp_mail() - get the Content-Type from the headers, if there is one,
5242         * then pass it through the wp_mail_content_type filter, in case a plugin
5243         * is handling changing the Content-Type.
5244         */
5245        $headers = array();
5246        if ( isset( $mail['headers'] ) ) {
5247                if ( is_array( $mail['headers'] ) ) {
5248                        $headers = $mail['headers'];
5249                } else {
5250                        $headers = explode( "\n", str_replace( "\r\n", "\n", $mail['headers'] ) );
5251                }
5252        }
5253
5254        foreach ( $headers as $header ) {
5255                if ( strpos($header, ':') === false ) {
5256                        continue;
5257                }
5258
5259                // Explode them out.
5260                list( $name, $content ) = explode( ':', trim( $header ), 2 );
5261
5262                // Cleanup crew.
5263                $name    = trim( $name    );
5264                $content = trim( $content );
5265
5266                if ( 'content-type' === strtolower( $name ) ) {
5267                        if ( strpos( $content, ';' ) !== false ) {
5268                                list( $type, $charset ) = explode( ';', $content );
5269                                $content_type = trim( $type );
5270                        } else {
5271                                $content_type = trim( $content );
5272                        }
5273                        break;
5274                }
5275        }
5276
5277        // Set Content-Type if we don't have a content-type from the input headers.
5278        if ( ! isset( $content_type ) ) {
5279                $content_type = 'text/plain';
5280        }
5281
5282        /** This filter is documented in wp-includes/pluggable.php */
5283        $content_type = apply_filters( 'wp_mail_content_type', $content_type );
5284
5285        if ( 'text/html' === $content_type ) {
5286                $mail['message'] = wp_staticize_emoji( $mail['message'] );
5287        }
5288
5289        return $mail;
5290}
5291
5292/**
5293 * Returns a regex string to match all emoji that WordPress recognises.
5294 *
5295 * This regex is automatically built from the regex in twemoji.js - if it needs to be updated,
5296 * you should update the regex there, then run the `grunt precommit:emoji` job.
5297 *
5298 * @since 4.8.1
5299 *
5300 * @param string $type Optional. Which regex type to return. Accepts 'codepoints' or 'entities', default 'coidepoints'.
5301 * @return string A regex to match all emoji that WordPress recognises.
5302 */
5303function wp_emoji_regex( $type = 'codepoints' ) {
5304        // If we're using a PCRE version that doesn't support Unicode, return a loose match regex.
5305        if ( 'codepoints' === $type && ( ! defined( 'PCRE_VERSION' ) || version_compare( PCRE_VERSION, '8.32', '<=' ) ) ) {
5306                return '/(
5307                     \xE2\x99[\x80-\x82]            # Male and female symbols
5308                   | [\x23\x30-\x39]\xE2\x83\xA3    # Digits
5309                   | \xF0\x9F[\x85-\x88][\xA6-\xBF] # Enclosed characters
5310                   | \xF0\x9F[\x8C-\x97][\x80-\xBF] # Misc
5311                   | \xF0\x9F\x98[\x80-\xBF]        # Smilies
5312                   | \xF0\x9F\x99[\x80-\x8F]
5313                   | \xF0\x9F[\xA4-\xA7][\x00-\xFF]
5314                   | \xF0\x9F\x9A[\x80-\xBF]        # Transport and map symbols
5315                   | \xE2\x80\x8D                   # Zero Width Joiner
5316                   | \xEF\xB8\x8F                   # Emoji Variation Selector
5317                )/x';
5318        }
5319
5320        // Do not remove the START/END comments - they're used to find where to insert the regex.
5321
5322        // START: emoji regex
5323        $codepoints = '/(\x{1f468}|\x{1f469}(?:\x{1f3fb}|\x{1f3fc}|\x{1f3fd}|\x{1f3fe}|\x{1f3ff})?\x{200d}(?:\x{2695}\x{fe0f}|\x{2696}\x{fe0f}|\x{2708}\x{fe0f}|\x{1f33e}|\x{1f373}|\x{1f393}|\x{1f3a4}|\x{1f3a8}|\x{1f3eb}|\x{1f3ed}|\x{1f4bb}|\x{1f4bc}|\x{1f527}|\x{1f52c}|\x{1f680}|\x{1f692})|(?:\x{1f3cb}|\x{1f3cc}|\x{1f575}|\x{26f9})(?:\x{fe0f}|\x{1f3fb}|\x{1f3fc}|\x{1f3fd}|\x{1f3fe}|\x{1f3ff})\x{200d}\x{2640}\x{fe0f}|\x{200d}\x{2642}\x{fe0f}|(?:\x{1f3c3}|\x{1f3c4}|\x{1f3ca}|\x{1f46e}|\x{1f471}|\x{1f473}|\x{1f477}|\x{1f481}|\x{1f482}|\x{1f486}|\x{1f487}|\x{1f645}|\x{1f646}|\x{1f647}|\x{1f64b}|\x{1f64d}|\x{1f64e}|\x{1f6a3}|\x{1f6b4}|\x{1f6b5}|\x{1f6b6}|\x{1f926}|\x{1f937}|\x{1f938}|\x{1f939}|\x{1f93d}|\x{1f93e}|\x{1f9d6}|\x{1f9d7}|\x{1f9d8}|\x{1f9d9}|\x{1f9da}|\x{1f9db}|\x{1f9dc}|\x{1f9dd})(?:\x{1f3fb}|\x{1f3fc}|\x{1f3fd}|\x{1f3fe}|\x{1f3ff})?\x{200d}\x{2640}\x{fe0f}|\x{200d}\x{2642}\x{fe0f}|\x{1f468}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f48b}\x{200d}\x{1f468}|\x{1f468}\x{200d}\x{1f468}\x{200d}\x{1f466}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f468}\x{200d}\x{1f467}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f468}\x{200d}\x{1f467}\x{200d}\x{1f467}|\x{1f468}\x{200d}\x{1f469}\x{200d}\x{1f466}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f467}|\x{1f469}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f48b}\x{200d}\x{1f468}|\x{1f469}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f48b}\x{200d}\x{1f469}|\x{1f469}\x{200d}\x{1f469}\x{200d}\x{1f466}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f467}|\x{1f468}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f468}|\x{1f468}\x{200d}\x{1f466}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f467}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f467}\x{200d}\x{1f467}|\x{1f468}\x{200d}\x{1f468}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f468}\x{200d}\x{1f467}|\x{1f468}\x{200d}\x{1f469}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f469}\x{200d}\x{1f467}|\x{1f469}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f468}|\x{1f469}\x{200d}\x{2764}\x{fe0f}\x{200d}\x{1f469}|\x{1f469}\x{200d}\x{1f466}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f467}\x{200d}\x{1f467}|\x{1f469}\x{200d}\x{1f469}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f469}\x{200d}\x{1f467}|\x{1f3f3}\x{fe0f}\x{200d}\x{1f308}|\x{1f3f4}\x{200d}\x{2620}\x{fe0f}|\x{1f441}\x{200d}\x{1f5e8}|\x{1f468}\x{200d}\x{1f466}|\x{1f468}\x{200d}\x{1f467}|\x{1f469}\x{200d}\x{1f466}|\x{1f469}\x{200d}\x{1f467}|\x{1f46f}\x{200d}\x{2640}\x{fe0f}|\x{1f46f}\x{200d}\x{2642}\x{fe0f}|\x{1f93c}\x{200d}\x{2640}\x{fe0f}|\x{1f93c}\x{200d}\x{2642}\x{fe0f}|\x{1f9de}\x{200d}\x{2640}\x{fe0f}|\x{1f9de}\x{200d}\x{2642}\x{fe0f}|\x{1f9df}\x{200d}\x{2640}\x{fe0f}|\x{1f9df}\x{200d}\x{2642}\x{fe0f}|(?:[\x{0023}\x{002a}\x{30}\x{31}\x{32}\x{33}\x{34}\x{35}\x{36}\x{37}\x{38}\x{39}])\x{fe0f}?\x{20e3}|(?:(?:\x{1f3cb}|\x{1f3cc}|\x{1f574}|\x{1f575}|\x{1f590}|[\x{261d}\x{26f7}\x{26f9}\x{270c}\x{270d}])(?:\x{fe0f}|(?!\x{fe0e}))|\x{1f385}|\x{1f3c2}|\x{1f3c3}|\x{1f3c4}|\x{1f3c7}|\x{1f3ca}|\x{1f442}|\x{1f443}|\x{1f446}|\x{1f447}|\x{1f448}|\x{1f449}|\x{1f44a}|\x{1f44b}|\x{1f44c}|\x{1f44d}|\x{1f44e}|\x{1f44f}|\x{1f450}|\x{1f466}|\x{1f467}|\x{1f468}|\x{1f469}|\x{1f46e}|\x{1f470}|\x{1f471}|\x{1f472}|\x{1f473}|\x{1f474}|\x{1f475}|\x{1f476}|\x{1f477}|\x{1f478}|\x{1f47c}|\x{1f481}|\x{1f482}|\x{1f483}|\x{1f485}|\x{1f486}|\x{1f487}|\x{1f4aa}|\x{1f57a}|\x{1f595}|\x{1f596}|\x{1f645}|\x{1f646}|\x{1f647}|\x{1f64b}|\x{1f64c}|\x{1f64d}|\x{1f64e}|\x{1f64f}|\x{1f6a3}|\x{1f6b4}|\x{1f6b5}|\x{1f6b6}|\x{1f6c0}|\x{1f6cc}|\x{1f918}|\x{1f919}|\x{1f91a}|\x{1f91b}|\x{1f91c}|\x{1f91e}|\x{1f91f}|\x{1f926}|\x{1f930}|\x{1f931}|\x{1f932}|\x{1f933}|\x{1f934}|\x{1f935}|\x{1f936}|\x{1f937}|\x{1f938}|\x{1f939}|\x{1f93d}|\x{1f93e}|\x{1f9d1}|\x{1f9d2}|\x{1f9d3}|\x{1f9d4}|\x{1f9d5}|\x{1f9d6}|\x{1f9d7}|\x{1f9d8}|\x{1f9d9}|\x{1f9da}|\x{1f9db}|\x{1f9dc}|\x{1f9dd}|[\x{270a}\x{270b}])(?:\x{1f3fb}|\x{1f3fc}|\x{1f3fd}|\x{1f3fe}|\x{1f3ff}|)|\x{1f3f4}\x{e0067}\x{e0062}\x{e0065}\x{e006e}\x{e0067}\x{e007f}|\x{1f3f4}\x{e0067}\x{e0062}\x{e0073}\x{e0063}\x{e0074}\x{e007f}|\x{1f3f4}\x{e0067}\x{e0062}\x{e0077}\x{e006c}\x{e0073}\x{e007f}|\x{1f1e6}\x{1f1e8}|\x{1f1e6}\x{1f1e9}|\x{1f1e6}\x{1f1ea}|\x{1f1e6}\x{1f1eb}|\x{1f1e6}\x{1f1ec}|\x{1f1e6}\x{1f1ee}|\x{1f1e6}\x{1f1f1}|\x{1f1e6}\x{1f1f2}|\x{1f1e6}\x{1f1f4}|\x{1f1e6}\x{1f1f6}|\x{1f1e6}\x{1f1f7}|\x{1f1e6}\x{1f1f8}|\x{1f1e6}\x{1f1f9}|\x{1f1e6}\x{1f1fa}|\x{1f1e6}\x{1f1fc}|\x{1f1e6}\x{1f1fd}|\x{1f1e6}\x{1f1ff}|\x{1f1e7}\x{1f1e6}|\x{1f1e7}\x{1f1e7}|\x{1f1e7}\x{1f1e9}|\x{1f1e7}\x{1f1ea}|\x{1f1e7}\x{1f1eb}|\x{1f1e7}\x{1f1ec}|\x{1f1e7}\x{1f1ed}|\x{1f1e7}\x{1f1ee}|\x{1f1e7}\x{1f1ef}|\x{1f1e7}\x{1f1f1}|\x{1f1e7}\x{1f1f2}|\x{1f1e7}\x{1f1f3}|\x{1f1e7}\x{1f1f4}|\x{1f1e7}\x{1f1f6}|\x{1f1e7}\x{1f1f7}|\x{1f1e7}\x{1f1f8}|\x{1f1e7}\x{1f1f9}|\x{1f1e7}\x{1f1fb}|\x{1f1e7}\x{1f1fc}|\x{1f1e7}\x{1f1fe}|\x{1f1e7}\x{1f1ff}|\x{1f1e8}\x{1f1e6}|\x{1f1e8}\x{1f1e8}|\x{1f1e8}\x{1f1e9}|\x{1f1e8}\x{1f1eb}|\x{1f1e8}\x{1f1ec}|\x{1f1e8}\x{1f1ed}|\x{1f1e8}\x{1f1ee}|\x{1f1e8}\x{1f1f0}|\x{1f1e8}\x{1f1f1}|\x{1f1e8}\x{1f1f2}|\x{1f1e8}\x{1f1f3}|\x{1f1e8}\x{1f1f4}|\x{1f1e8}\x{1f1f5}|\x{1f1e8}\x{1f1f7}|\x{1f1e8}\x{1f1fa}|\x{1f1e8}\x{1f1fb}|\x{1f1e8}\x{1f1fc}|\x{1f1e8}\x{1f1fd}|\x{1f1e8}\x{1f1fe}|\x{1f1e8}\x{1f1ff}|\x{1f1e9}\x{1f1ea}|\x{1f1e9}\x{1f1ec}|\x{1f1e9}\x{1f1ef}|\x{1f1e9}\x{1f1f0}|\x{1f1e9}\x{1f1f2}|\x{1f1e9}\x{1f1f4}|\x{1f1e9}\x{1f1ff}|\x{1f1ea}\x{1f1e6}|\x{1f1ea}\x{1f1e8}|\x{1f1ea}\x{1f1ea}|\x{1f1ea}\x{1f1ec}|\x{1f1ea}\x{1f1ed}|\x{1f1ea}\x{1f1f7}|\x{1f1ea}\x{1f1f8}|\x{1f1ea}\x{1f1f9}|\x{1f1ea}\x{1f1fa}|\x{1f1eb}\x{1f1ee}|\x{1f1eb}\x{1f1ef}|\x{1f1eb}\x{1f1f0}|\x{1f1eb}\x{1f1f2}|\x{1f1eb}\x{1f1f4}|\x{1f1eb}\x{1f1f7}|\x{1f1ec}\x{1f1e6}|\x{1f1ec}\x{1f1e7}|\x{1f1ec}\x{1f1e9}|\x{1f1ec}\x{1f1ea}|\x{1f1ec}\x{1f1eb}|\x{1f1ec}\x{1f1ec}|\x{1f1ec}\x{1f1ed}|\x{1f1ec}\x{1f1ee}|\x{1f1ec}\x{1f1f1}|\x{1f1ec}\x{1f1f2}|\x{1f1ec}\x{1f1f3}|\x{1f1ec}\x{1f1f5}|\x{1f1ec}\x{1f1f6}|\x{1f1ec}\x{1f1f7}|\x{1f1ec}\x{1f1f8}|\x{1f1ec}\x{1f1f9}|\x{1f1ec}\x{1f1fa}|\x{1f1ec}\x{1f1fc}|\x{1f1ec}\x{1f1fe}|\x{1f1ed}\x{1f1f0}|\x{1f1ed}\x{1f1f2}|\x{1f1ed}\x{1f1f3}|\x{1f1ed}\x{1f1f7}|\x{1f1ed}\x{1f1f9}|\x{1f1ed}\x{1f1fa}|\x{1f1ee}\x{1f1e8}|\x{1f1ee}\x{1f1e9}|\x{1f1ee}\x{1f1ea}|\x{1f1ee}\x{1f1f1}|\x{1f1ee}\x{1f1f2}|\x{1f1ee}\x{1f1f3}|\x{1f1ee}\x{1f1f4}|\x{1f1ee}\x{1f1f6}|\x{1f1ee}\x{1f1f7}|\x{1f1ee}\x{1f1f8}|\x{1f1ee}\x{1f1f9}|\x{1f1ef}\x{1f1ea}|\x{1f1ef}\x{1f1f2}|\x{1f1ef}\x{1f1f4}|\x{1f1ef}\x{1f1f5}|\x{1f1f0}\x{1f1ea}|\x{1f1f0}\x{1f1ec}|\x{1f1f0}\x{1f1ed}|\x{1f1f0}\x{1f1ee}|\x{1f1f0}\x{1f1f2}|\x{1f1f0}\x{1f1f3}|\x{1f1f0}\x{1f1f5}|\x{1f1f0}\x{1f1f7}|\x{1f1f0}\x{1f1fc}|\x{1f1f0}\x{1f1fe}|\x{1f1f0}\x{1f1ff}|\x{1f1f1}\x{1f1e6}|\x{1f1f1}\x{1f1e7}|\x{1f1f1}\x{1f1e8}|\x{1f1f1}\x{1f1ee}|\x{1f1f1}\x{1f1f0}|\x{1f1f1}\x{1f1f7}|\x{1f1f1}\x{1f1f8}|\x{1f1f1}\x{1f1f9}|\x{1f1f1}\x{1f1fa}|\x{1f1f1}\x{1f1fb}|\x{1f1f1}\x{1f1fe}|\x{1f1f2}\x{1f1e6}|\x{1f1f2}\x{1f1e8}|\x{1f1f2}\x{1f1e9}|\x{1f1f2}\x{1f1ea}|\x{1f1f2}\x{1f1eb}|\x{1f1f2}\x{1f1ec}|\x{1f1f2}\x{1f1ed}|\x{1f1f2}\x{1f1f0}|\x{1f1f2}\x{1f1f1}|\x{1f1f2}\x{1f1f2}|\x{1f1f2}\x{1f1f3}|\x{1f1f2}\x{1f1f4}|\x{1f1f2}\x{1f1f5}|\x{1f1f2}\x{1f1f6}|\x{1f1f2}\x{1f1f7}|\x{1f1f2}\x{1f1f8}|\x{1f1f2}\x{1f1f9}|\x{1f1f2}\x{1f1fa}|\x{1f1f2}\x{1f1fb}|\x{1f1f2}\x{1f1fc}|\x{1f1f2}\x{1f1fd}|\x{1f1f2}\x{1f1fe}|\x{1f1f2}\x{1f1ff}|\x{1f1f3}\x{1f1e6}|\x{1f1f3}\x{1f1e8}|\x{1f1f3}\x{1f1ea}|\x{1f1f3}\x{1f1eb}|\x{1f1f3}\x{1f1ec}|\x{1f1f3}\x{1f1ee}|\x{1f1f3}\x{1f1f1}|\x{1f1f3}\x{1f1f4}|\x{1f1f3}\x{1f1f5}|\x{1f1f3}\x{1f1f7}|\x{1f1f3}\x{1f1fa}|\x{1f1f3}\x{1f1ff}|\x{1f1f4}\x{1f1f2}|\x{1f1f5}\x{1f1e6}|\x{1f1f5}\x{1f1ea}|\x{1f1f5}\x{1f1eb}|\x{1f1f5}\x{1f1ec}|\x{1f1f5}\x{1f1ed}|\x{1f1f5}\x{1f1f0}|\x{1f1f5}\x{1f1f1}|\x{1f1f5}\x{1f1f2}|\x{1f1f5}\x{1f1f3}|\x{1f1f5}\x{1f1f7}|\x{1f1f5}\x{1f1f8}|\x{1f1f5}\x{1f1f9}|\x{1f1f5}\x{1f1fc}|\x{1f1f5}\x{1f1fe}|\x{1f1f6}\x{1f1e6}|\x{1f1f7}\x{1f1ea}|\x{1f1f7}\x{1f1f4}|\x{1f1f7}\x{1f1f8}|\x{1f1f7}\x{1f1fa}|\x{1f1f7}\x{1f1fc}|\x{1f1f8}\x{1f1e6}|\x{1f1f8}\x{1f1e7}|\x{1f1f8}\x{1f1e8}|\x{1f1f8}\x{1f1e9}|\x{1f1f8}\x{1f1ea}|\x{1f1f8}\x{1f1ec}|\x{1f1f8}\x{1f1ed}|\x{1f1f8}\x{1f1ee}|\x{1f1f8}\x{1f1ef}|\x{1f1f8}\x{1f1f0}|\x{1f1f8}\x{1f1f1}|\x{1f1f8}\x{1f1f2}|\x{1f1f8}\x{1f1f3}|\x{1f1f8}\x{1f1f4}|\x{1f1f8}\x{1f1f7}|\x{1f1f8}\x{1f1f8}|\x{1f1f8}\x{1f1f9}|\x{1f1f8}\x{1f1fb}|\x{1f1f8}\x{1f1fd}|\x{1f1f8}\x{1f1fe}|\x{1f1f8}\x{1f1ff}|\x{1f1f9}\x{1f1e6}|\x{1f1f9}\x{1f1e8}|\x{1f1f9}\x{1f1e9}|\x{1f1f9}\x{1f1eb}|\x{1f1f9}\x{1f1ec}|\x{1f1f9}\x{1f1ed}|\x{1f1f9}\x{1f1ef}|\x{1f1f9}\x{1f1f0}|\x{1f1f9}\x{1f1f1}|\x{1f1f9}\x{1f1f2}|\x{1f1f9}\x{1f1f3}|\x{1f1f9}\x{1f1f4}|\x{1f1f9}\x{1f1f7}|\x{1f1f9}\x{1f1f9}|\x{1f1f9}\x{1f1fb}|\x{1f1f9}\x{1f1fc}|\x{1f1f9}\x{1f1ff}|\x{1f1fa}\x{1f1e6}|\x{1f1fa}\x{1f1ec}|\x{1f1fa}\x{1f1f2}|\x{1f1fa}\x{1f1f3}|\x{1f1fa}\x{1f1f8}|\x{1f1fa}\x{1f1fe}|\x{1f1fa}\x{1f1ff}|\x{1f1fb}\x{1f1e6}|\x{1f1fb}\x{1f1e8}|\x{1f1fb}\x{1f1ea}|\x{1f1fb}\x{1f1ec}|\x{1f1fb}\x{1f1ee}|\x{1f1fb}\x{1f1f3}|\x{1f1fb}\x{1f1fa}|\x{1f1fc}\x{1f1eb}|\x{1f1fc}\x{1f1f8}|\x{1f1fd}\x{1f1f0}|\x{1f1fe}\x{1f1ea}|\x{1f1fe}\x{1f1f9}|\x{1f1ff}\x{1f1e6}|\x{1f1ff}\x{1f1f2}|\x{1f1ff}\x{1f1fc}|\x{10000}|\x{1f0cf}|\x{1f18e}|\x{1f191}|\x{1f192}|\x{1f193}|\x{1f194}|\x{1f195}|\x{1f196}|\x{1f197}|\x{1f198}|\x{1f199}|\x{1f19a}|\x{1f1e6}|\x{1f1e7}|\x{1f1e8}|\x{1f1e9}|\x{1f1ea}|\x{1f1eb}|\x{1f1ec}|\x{1f1ed}|\x{1f1ee}|\x{1f1ef}|\x{1f1f0}|\x{1f1f1}|\x{1f1f2}|\x{1f1f3}|\x{1f1f4}|\x{1f1f5}|\x{1f1f6}|\x{1f1f7}|\x{1f1f8}|\x{1f1f9}|\x{1f1fa}|\x{1f1fb}|\x{1f1fc}|\x{1f1fd}|\x{1f1fe}|\x{1f1ff}|\x{1f201}|\x{1f232}|\x{1f233}|\x{1f234}|\x{1f235}|\x{1f236}|\x{1f238}|\x{1f239}|\x{1f23a}|\x{1f250}|\x{1f251}|\x{1f300}|\x{1f301}|\x{1f302}|\x{1f303}|\x{1f304}|\x{1f305}|\x{1f306}|\x{1f307}|\x{1f308}|\x{1f309}|\x{1f30a}|\x{1f30b}|\x{1f30c}|\x{1f30d}|\x{1f30e}|\x{1f30f}|\x{1f310}|\x{1f311}|\x{1f312}|\x{1f313}|\x{1f314}|\x{1f315}|\x{1f316}|\x{1f317}|\x{1f318}|\x{1f319}|\x{1f31a}|\x{1f31b}|\x{1f31c}|\x{1f31d}|\x{1f31e}|\x{1f31f}|\x{1f320}|\x{1f32d}|\x{1f32e}|\x{1f32f}|\x{1f330}|\x{1f331}|\x{1f332}|\x{1f333}|\x{1f334}|\x{1f335}|\x{1f337}|\x{1f338}|\x{1f339}|\x{1f33a}|\x{1f33b}|\x{1f33c}|\x{1f33d}|\x{1f33e}|\x{1f33f}|\x{1f340}|\x{1f341}|\x{1f342}|\x{1f343}|\x{1f344}|\x{1f345}|\x{1f346}|\x{1f347}|\x{1f348}|\x{1f349}|\x{1f34a}|\x{1f34b}|\x{1f34c}|\x{1f34d}|\x{1f34e}|\x{1f34f}|\x{1f350}|\x{1f351}|\x{1f352}|\x{1f353}|\x{1f354}|\x{1f355}|\x{1f356}|\x{1f357}|\x{1f358}|\x{1f359}|\x{1f35a}|\x{1f35b}|\x{1f35c}|\x{1f35d}|\x{1f35e}|\x{1f35f}|\x{1f360}|\x{1f361}|\x{1f362}|\x{1f363}|\x{1f364}|\x{1f365}|\x{1f366}|\x{1f367}|\x{1f368}|\x{1f369}|\x{1f36a}|\x{1f36b}|\x{1f36c}|\x{1f36d}|\x{1f36e}|\x{1f36f}|\x{1f370}|\x{1f371}|\x{1f372}|\x{1f373}|\x{1f374}|\x{1f375}|\x{1f376}|\x{1f377}|\x{1f378}|\x{1f379}|\x{1f37a}|\x{1f37b}|\x{1f37c}|\x{1f37e}|\x{1f37f}|\x{1f380}|\x{1f381}|\x{1f382}|\x{1f383}|\x{1f384}|\x{1f386}|\x{1f387}|\x{1f388}|\x{1f389}|\x{1f38a}|\x{1f38b}|\x{1f38c}|\x{1f38d}|\x{1f38e}|\x{1f38f}|\x{1f390}|\x{1f391}|\x{1f392}|\x{1f393}|\x{1f3a0}|\x{1f3a1}|\x{1f3a2}|\x{1f3a3}|\x{1f3a4}|\x{1f3a5}|\x{1f3a6}|\x{1f3a7}|\x{1f3a8}|\x{1f3a9}|\x{1f3aa}|\x{1f3ab}|\x{1f3ac}|\x{1f3ad}|\x{1f3ae}|\x{1f3af}|\x{1f3b0}|\x{1f3b1}|\x{1f3b2}|\x{1f3b3}|\x{1f3b4}|\x{1f3b5}|\x{1f3b6}|\x{1f3b7}|\x{1f3b8}|\x{1f3b9}|\x{1f3ba}|\x{1f3bb}|\x{1f3bc}|\x{1f3bd}|\x{1f3be}|\x{1f3bf}|\x{1f3c0}|\x{1f3c1}|\x{1f3c5}|\x{1f3c6}|\x{1f3c8}|\x{1f3c9}|\x{1f3cf}|\x{1f3d0}|\x{1f3d1}|\x{1f3d2}|\x{1f3d3}|\x{1f3e0}|\x{1f3e1}|\x{1f3e2}|\x{1f3e3}|\x{1f3e4}|\x{1f3e5}|\x{1f3e6}|\x{1f3e7}|\x{1f3e8}|\x{1f3e9}|\x{1f3ea}|\x{1f3eb}|\x{1f3ec}|\x{1f3ed}|\x{1f3ee}|\x{1f3ef}|\x{1f3f0}|\x{1f3f4}|\x{1f3f8}|\x{1f3f9}|\x{1f3fa}|\x{1f3fb}|\x{1f3fc}|\x{1f3fd}|\x{1f3fe}|\x{1f3ff}|\x{1f400}|\x{1f401}|\x{1f402}|\x{1f403}|\x{1f404}|\x{1f405}|\x{1f406}|\x{1f407}|\x{1f408}|\x{1f409}|\x{1f40a}|\x{1f40b}|\x{1f40c}|\x{1f40d}|\x{1f40e}|\x{1f40f}|\x{1f410}|\x{1f411}|\x{1f412}|\x{1f413}|\x{1f414}|\x{1f415}|\x{1f416}|\x{1f417}|\x{1f418}|\x{1f419}|\x{1f41a}|\x{1f41b}|\x{1f41c}|\x{1f41d}|\x{1f41e}|\x{1f41f}|\x{1f420}|\x{1f421}|\x{1f422}|\x{1f423}|\x{1f424}|\x{1f425}|\x{1f426}|\x{1f427}|\x{1f428}|\x{1f429}|\x{1f42a}|\x{1f42b}|\x{1f42c}|\x{1f42d}|\x{1f42e}|\x{1f42f}|\x{1f430}|\x{1f431}|\x{1f432}|\x{1f433}|\x{1f434}|\x{1f435}|\x{1f436}|\x{1f437}|\x{1f438}|\x{1f439}|\x{1f43a}|\x{1f43b}|\x{1f43c}|\x{1f43d}|\x{1f43e}|\x{1f440}|\x{1f444}|\x{1f445}|\x{1f451}|\x{1f452}|\x{1f453}|\x{1f454}|\x{1f455}|\x{1f456}|\x{1f457}|\x{1f458}|\x{1f459}|\x{1f45a}|\x{1f45b}|\x{1f45c}|\x{1f45d}|\x{1f45e}|\x{1f45f}|\x{1f460}|\x{1f461}|\x{1f462}|\x{1f463}|\x{1f464}|\x{1f465}|\x{1f46a}|\x{1f46b}|\x{1f46c}|\x{1f46d}|\x{1f46f}|\x{1f479}|\x{1f47a}|\x{1f47b}|\x{1f47d}|\x{1f47e}|\x{1f47f}|\x{1f480}|\x{1f484}|\x{1f488}|\x{1f489}|\x{1f48a}|\x{1f48b}|\x{1f48c}|\x{1f48d}|\x{1f48e}|\x{1f48f}|\x{1f490}|\x{1f491}|\x{1f492}|\x{1f493}|\x{1f494}|\x{1f495}|\x{1f496}|\x{1f497}|\x{1f498}|\x{1f499}|\x{1f49a}|\x{1f49b}|\x{1f49c}|\x{1f49d}|\x{1f49e}|\x{1f49f}|\x{1f4a0}|\x{1f4a1}|\x{1f4a2}|\x{1f4a3}|\x{1f4a4}|\x{1f4a5}|\x{1f4a6}|\x{1f4a7}|\x{1f4a8}|\x{1f4a9}|\x{1f4ab}|\x{1f4ac}|\x{1f4ad}|\x{1f4ae}|\x{1f4af}|\x{1f4b0}|\x{1f4b1}|\x{1f4b2}|\x{1f4b3}|\x{1f4b4}|\x{1f4b5}|\x{1f4b6}|\x{1f4b7}|\x{1f4b8}|\x{1f4b9}|\x{1f4ba}|\x{1f4bb}|\x{1f4bc}|\x{1f4bd}|\x{1f4be}|\x{1f4bf}|\x{1f4c0}|\x{1f4c1}|\x{1f4c2}|\x{1f4c3}|\x{1f4c4}|\x{1f4c5}|\x{1f4c6}|\x{1f4c7}|\x{1f4c8}|\x{1f4c9}|\x{1f4ca}|\x{1f4cb}|\x{1f4cc}|\x{1f4cd}|\x{1f4ce}|\x{1f4cf}|\x{1f4d0}|\x{1f4d1}|\x{1f4d2}|\x{1f4d3}|\x{1f4d4}|\x{1f4d5}|\x{1f4d6}|\x{1f4d7}|\x{1f4d8}|\x{1f4d9}|\x{1f4da}|\x{1f4db}|\x{1f4dc}|\x{1f4dd}|\x{1f4de}|\x{1f4df}|\x{1f4e0}|\x{1f4e1}|\x{1f4e2}|\x{1f4e3}|\x{1f4e4}|\x{1f4e5}|\x{1f4e6}|\x{1f4e7}|\x{1f4e8}|\x{1f4e9}|\x{1f4ea}|\x{1f4eb}|\x{1f4ec}|\x{1f4ed}|\x{1f4ee}|\x{1f4ef}|\x{1f4f0}|\x{1f4f1}|\x{1f4f2}|\x{1f4f3}|\x{1f4f4}|\x{1f4f5}|\x{1f4f6}|\x{1f4f7}|\x{1f4f8}|\x{1f4f9}|\x{1f4fa}|\x{1f4fb}|\x{1f4fc}|\x{1f4ff}|\x{1f500}|\x{1f501}|\x{1f502}|\x{1f503}|\x{1f504}|\x{1f505}|\x{1f506}|\x{1f507}|\x{1f508}|\x{1f509}|\x{1f50a}|\x{1f50b}|\x{1f50c}|\x{1f50d}|\x{1f50e}|\x{1f50f}|\x{1f510}|\x{1f511}|\x{1f512}|\x{1f513}|\x{1f514}|\x{1f515}|\x{1f516}|\x{1f517}|\x{1f518}|\x{1f519}|\x{1f51a}|\x{1f51b}|\x{1f51c}|\x{1f51d}|\x{1f51e}|\x{1f51f}|\x{1f520}|\x{1f521}|\x{1f522}|\x{1f523}|\x{1f524}|\x{1f525}|\x{1f526}|\x{1f527}|\x{1f528}|\x{1f529}|\x{1f52a}|\x{1f52b}|\x{1f52c}|\x{1f52d}|\x{1f52e}|\x{1f52f}|\x{1f530}|\x{1f531}|\x{1f532}|\x{1f533}|\x{1f534}|\x{1f535}|\x{1f536}|\x{1f537}|\x{1f538}|\x{1f539}|\x{1f53a}|\x{1f53b}|\x{1f53c}|\x{1f53d}|\x{1f54b}|\x{1f54c}|\x{1f54d}|\x{1f54e}|\x{1f550}|\x{1f551}|\x{1f552}|\x{1f553}|\x{1f554}|\x{1f555}|\x{1f556}|\x{1f557}|\x{1f558}|\x{1f559}|\x{1f55a}|\x{1f55b}|\x{1f55c}|\x{1f55d}|\x{1f55e}|\x{1f55f}|\x{1f560}|\x{1f561}|\x{1f562}|\x{1f563}|\x{1f564}|\x{1f565}|\x{1f566}|\x{1f567}|\x{1f5a4}|\x{1f5fb}|\x{1f5fc}|\x{1f5fd}|\x{1f5fe}|\x{1f5ff}|\x{1f600}|\x{1f601}|\x{1f602}|\x{1f603}|\x{1f604}|\x{1f605}|\x{1f606}|\x{1f607}|\x{1f608}|\x{1f609}|\x{1f60a}|\x{1f60b}|\x{1f60c}|\x{1f60d}|\x{1f60e}|\x{1f60f}|\x{1f610}|\x{1f611}|\x{1f612}|\x{1f613}|\x{1f614}|\x{1f615}|\x{1f616}|\x{1f617}|\x{1f618}|\x{1f619}|\x{1f61a}|\x{1f61b}|\x{1f61c}|\x{1f61d}|\x{1f61e}|\x{1f61f}|\x{1f620}|\x{1f621}|\x{1f622}|\x{1f623}|\x{1f624}|\x{1f625}|\x{1f626}|\x{1f627}|\x{1f628}|\x{1f629}|\x{1f62a}|\x{1f62b}|\x{1f62c}|\x{1f62d}|\x{1f62e}|\x{1f62f}|\x{1f630}|\x{1f631}|\x{1f632}|\x{1f633}|\x{1f634}|\x{1f635}|\x{1f636}|\x{1f637}|\x{1f638}|\x{1f639}|\x{1f63a}|\x{1f63b}|\x{1f63c}|\x{1f63d}|\x{1f63e}|\x{1f63f}|\x{1f640}|\x{1f641}|\x{1f642}|\x{1f643}|\x{1f644}|\x{1f648}|\x{1f649}|\x{1f64a}|\x{1f680}|\x{1f681}|\x{1f682}|\x{1f683}|\x{1f684}|\x{1f685}|\x{1f686}|\x{1f687}|\x{1f688}|\x{1f689}|\x{1f68a}|\x{1f68b}|\x{1f68c}|\x{1f68d}|\x{1f68e}|\x{1f68f}|\x{1f690}|\x{1f691}|\x{1f692}|\x{1f693}|\x{1f694}|\x{1f695}|\x{1f696}|\x{1f697}|\x{1f698}|\x{1f699}|\x{1f69a}|\x{1f69b}|\x{1f69c}|\x{1f69d}|\x{1f69e}|\x{1f69f}|\x{1f6a0}|\x{1f6a1}|\x{1f6a2}|\x{1f6a4}|\x{1f6a5}|\x{1f6a6}|\x{1f6a7}|\x{1f6a8}|\x{1f6a9}|\x{1f6aa}|\x{1f6ab}|\x{1f6ac}|\x{1f6ad}|\x{1f6ae}|\x{1f6af}|\x{1f6b0}|\x{1f6b1}|\x{1f6b2}|\x{1f6b3}|\x{1f6b7}|\x{1f6b8}|\x{1f6b9}|\x{1f6ba}|\x{1f6bb}|\x{1f6bc}|\x{1f6bd}|\x{1f6be}|\x{1f6bf}|\x{1f6c1}|\x{1f6c2}|\x{1f6c3}|\x{1f6c4}|\x{1f6c5}|\x{1f6d0}|\x{1f6d1}|\x{1f6d2}|\x{1f6eb}|\x{1f6ec}|\x{1f6f4}|\x{1f6f5}|\x{1f6f6}|\x{1f6f7}|\x{1f6f8}|\x{1f910}|\x{1f911}|\x{1f912}|\x{1f913}|\x{1f914}|\x{1f915}|\x{1f916}|\x{1f917}|\x{1f91d}|\x{1f920}|\x{1f921}|\x{1f922}|\x{1f923}|\x{1f924}|\x{1f925}|\x{1f927}|\x{1f928}|\x{1f929}|\x{1f92a}|\x{1f92b}|\x{1f92c}|\x{1f92d}|\x{1f92e}|\x{1f92f}|\x{1f93a}|\x{1f93c}|\x{1f940}|\x{1f941}|\x{1f942}|\x{1f943}|\x{1f944}|\x{1f945}|\x{1f947}|\x{1f948}|\x{1f949}|\x{1f94a}|\x{1f94b}|\x{1f94c}|\x{1f950}|\x{1f951}|\x{1f952}|\x{1f953}|\x{1f954}|\x{1f955}|\x{1f956}|\x{1f957}|\x{1f958}|\x{1f959}|\x{1f95a}|\x{1f95b}|\x{1f95c}|\x{1f95d}|\x{1f95e}|\x{1f95f}|\x{1f960}|\x{1f961}|\x{1f962}|\x{1f963}|\x{1f964}|\x{1f965}|\x{1f966}|\x{1f967}|\x{1f968}|\x{1f969}|\x{1f96a}|\x{1f96b}|\x{1f980}|\x{1f981}|\x{1f982}|\x{1f983}|\x{1f984}|\x{1f985}|\x{1f986}|\x{1f987}|\x{1f988}|\x{1f989}|\x{1f98a}|\x{1f98b}|\x{1f98c}|\x{1f98d}|\x{1f98e}|\x{1f98f}|\x{1f990}|\x{1f991}|\x{1f992}|\x{1f993}|\x{1f994}|\x{1f995}|\x{1f996}|\x{1f997}|\x{1f9c0}|\x{1f9d0}|\x{1f9de}|\x{1f9df}|\x{1f9e0}|\x{1f9e1}|\x{1f9e2}|\x{1f9e3}|\x{1f9e4}|\x{1f9e5}|\x{1f9e6}|[\x{23e9}\x{23ea}\x{23eb}\x{23ec}\x{23f0}\x{23f3}\x{2640}\x{2642}\x{2695}\x{26ce}\x{2705}\x{2728}\x{274c}\x{274e}\x{2753}\x{2754}\x{2755}\x{2795}\x{2796}\x{2797}\x{27b0}\x{27bf}\x{e50a}]|(?:\x{1f004}|\x{1f170}|\x{1f171}|\x{1f17e}|\x{1f17f}|\x{1f202}|\x{1f21a}|\x{1f22f}|\x{1f237}|\x{1f321}|\x{1f324}|\x{1f325}|\x{1f326}|\x{1f327}|\x{1f328}|\x{1f329}|\x{1f32a}|\x{1f32b}|\x{1f32c}|\x{1f336}|\x{1f37d}|\x{1f396}|\x{1f397}|\x{1f399}|\x{1f39a}|\x{1f39b}|\x{1f39e}|\x{1f39f}|\x{1f3cd}|\x{1f3ce}|\x{1f3d4}|\x{1f3d5}|\x{1f3d6}|\x{1f3d7}|\x{1f3d8}|\x{1f3d9}|\x{1f3da}|\x{1f3db}|\x{1f3dc}|\x{1f3dd}|\x{1f3de}|\x{1f3df}|\x{1f3f3}|\x{1f3f5}|\x{1f3f7}|\x{1f43f}|\x{1f441}|\x{1f4fd}|\x{1f549}|\x{1f54a}|\x{1f56f}|\x{1f570}|\x{1f573}|\x{1f576}|\x{1f577}|\x{1f578}|\x{1f579}|\x{1f587}|\x{1f58a}|\x{1f58b}|\x{1f58c}|\x{1f58d}|\x{1f5a5}|\x{1f5a8}|\x{1f5b1}|\x{1f5b2}|\x{1f5bc}|\x{1f5c2}|\x{1f5c3}|\x{1f5c4}|\x{1f5d1}|\x{1f5d2}|\x{1f5d3}|\x{1f5dc}|\x{1f5dd}|\x{1f5de}|\x{1f5e1}|\x{1f5e3}|\x{1f5e8}|\x{1f5ef}|\x{1f5f3}|\x{1f5fa}|\x{1f6cb}|\x{1f6cd}|\x{1f6ce}|\x{1f6cf}|\x{1f6e0}|\x{1f6e1}|\x{1f6e2}|\x{1f6e3}|\x{1f6e4}|\x{1f6e5}|\x{1f6e9}|\x{1f6f0}|\x{1f6f3}|[\x{00a9}\x{00ae}\x{203c}\x{2049}\x{2122}\x{2139}\x{2194}\x{2195}\x{2196}\x{2197}\x{2198}\x{2199}\x{21a9}\x{21aa}\x{231a}\x{231b}\x{2328}\x{23cf}\x{23ed}\x{23ee}\x{23ef}\x{23f1}\x{23f2}\x{23f8}\x{23f9}\x{23fa}\x{24c2}\x{25aa}\x{25ab}\x{25b6}\x{25c0}\x{25fb}\x{25fc}\x{25fd}\x{25fe}\x{2600}\x{2601}\x{2602}\x{2603}\x{2604}\x{260e}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262a}\x{262e}\x{262f}\x{2638}\x{2639}\x{263a}\x{2648}\x{2649}\x{264a}\x{264b}\x{264c}\x{264d}\x{264e}\x{264f}\x{2650}\x{2651}\x{2652}\x{2653}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267b}\x{267f}\x{2692}\x{2693}\x{2694}\x{2696}\x{2697}\x{2699}\x{269b}\x{269c}\x{26a0}\x{26a1}\x{26aa}\x{26ab}\x{26b0}\x{26b1}\x{26bd}\x{26be}\x{26c4}\x{26c5}\x{26c8}\x{26cf}\x{26d1}\x{26d3}\x{26d4}\x{26e9}\x{26ea}\x{26f0}\x{26f1}\x{26f2}\x{26f3}\x{26f4}\x{26f5}\x{26f8}\x{26fa}\x{26fd}\x{2702}\x{2708}\x{2709}\x{270f}\x{2712}\x{2714}\x{2716}\x{271d}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2757}\x{2763}\x{2764}\x{27a1}\x{2934}\x{2935}\x{2b05}\x{2b06}\x{2b07}\x{2b1b}\x{2b1c}\x{2b50}\x{2b55}\x{3030}\x{303d}\x{3297}\x{3299}])(?:\x{fe0f}|(?!\x{fe0e})))/u';
5324        $entities = '/(&#x1f468;|&#x1f469;(?:&#x1f3fb;|&#x1f3fc;|&#x1f3fd;|&#x1f3fe;|&#x1f3ff;)?&#x200d;(?:&#x2695;&#xfe0f;|&#x2696;&#xfe0f;|&#x2708;&#xfe0f;|&#x1f33e;|&#x1f373;|&#x1f393;|&#x1f3a4;|&#x1f3a8;|&#x1f3eb;|&#x1f3ed;|&#x1f4bb;|&#x1f4bc;|&#x1f527;|&#x1f52c;|&#x1f680;|&#x1f692;)|(?:&#x1f3cb;|&#x1f3cc;|&#x1f575;|&#x26f9;)(?:&#xfe0f;|&#x1f3fb;|&#x1f3fc;|&#x1f3fd;|&#x1f3fe;|&#x1f3ff;)&#x200d;&#x2640;&#xfe0f;|&#x200d;&#x2642;&#xfe0f;|(?:&#x1f3c3;|&#x1f3c4;|&#x1f3ca;|&#x1f46e;|&#x1f471;|&#x1f473;|&#x1f477;|&#x1f481;|&#x1f482;|&#x1f486;|&#x1f487;|&#x1f645;|&#x1f646;|&#x1f647;|&#x1f64b;|&#x1f64d;|&#x1f64e;|&#x1f6a3;|&#x1f6b4;|&#x1f6b5;|&#x1f6b6;|&#x1f926;|&#x1f937;|&#x1f938;|&#x1f939;|&#x1f93d;|&#x1f93e;|&#x1f9d6;|&#x1f9d7;|&#x1f9d8;|&#x1f9d9;|&#x1f9da;|&#x1f9db;|&#x1f9dc;|&#x1f9dd;)(?:&#x1f3fb;|&#x1f3fc;|&#x1f3fd;|&#x1f3fe;|&#x1f3ff;)?&#x200d;&#x2640;&#xfe0f;|&#x200d;&#x2642;&#xfe0f;|&#x1f468;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f48b;&#x200d;&#x1f468;|&#x1f468;&#x200d;&#x1f468;&#x200d;&#x1f466;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f468;&#x200d;&#x1f467;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f468;&#x200d;&#x1f467;&#x200d;&#x1f467;|&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f466;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f467;|&#x1f469;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f48b;&#x200d;&#x1f468;|&#x1f469;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f48b;&#x200d;&#x1f469;|&#x1f469;&#x200d;&#x1f469;&#x200d;&#x1f466;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f467;|&#x1f468;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f468;|&#x1f468;&#x200d;&#x1f466;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f467;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f467;&#x200d;&#x1f467;|&#x1f468;&#x200d;&#x1f468;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f468;&#x200d;&#x1f467;|&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f469;&#x200d;&#x1f467;|&#x1f469;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f468;|&#x1f469;&#x200d;&#x2764;&#xfe0f;&#x200d;&#x1f469;|&#x1f469;&#x200d;&#x1f466;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f467;&#x200d;&#x1f467;|&#x1f469;&#x200d;&#x1f469;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f469;&#x200d;&#x1f467;|&#x1f3f3;&#xfe0f;&#x200d;&#x1f308;|&#x1f3f4;&#x200d;&#x2620;&#xfe0f;|&#x1f441;&#x200d;&#x1f5e8;|&#x1f468;&#x200d;&#x1f466;|&#x1f468;&#x200d;&#x1f467;|&#x1f469;&#x200d;&#x1f466;|&#x1f469;&#x200d;&#x1f467;|&#x1f46f;&#x200d;&#x2640;&#xfe0f;|&#x1f46f;&#x200d;&#x2642;&#xfe0f;|&#x1f93c;&#x200d;&#x2640;&#xfe0f;|&#x1f93c;&#x200d;&#x2642;&#xfe0f;|&#x1f9de;&#x200d;&#x2640;&#xfe0f;|&#x1f9de;&#x200d;&#x2642;&#xfe0f;|&#x1f9df;&#x200d;&#x2640;&#xfe0f;|&#x1f9df;&#x200d;&#x2642;&#xfe0f;|(?:(?:&#x0023;|&#x002a;|&#x30;|&#x31;|&#x32;|&#x33;|&#x34;|&#x35;|&#x36;|&#x37;|&#x38;|&#x39;))&#xfe0f;?&#x20e3;|(?:(?:&#x1f3cb;|&#x1f3cc;|&#x1f574;|&#x1f575;|&#x1f590;|(?:&#x261d;|&#x26f7;|&#x26f9;|&#x270c;|&#x270d;))(?:&#xfe0f;|(?!&#xfe0e;))|&#x1f385;|&#x1f3c2;|&#x1f3c3;|&#x1f3c4;|&#x1f3c7;|&#x1f3ca;|&#x1f442;|&#x1f443;|&#x1f446;|&#x1f447;|&#x1f448;|&#x1f449;|&#x1f44a;|&#x1f44b;|&#x1f44c;|&#x1f44d;|&#x1f44e;|&#x1f44f;|&#x1f450;|&#x1f466;|&#x1f467;|&#x1f468;|&#x1f469;|&#x1f46e;|&#x1f470;|&#x1f471;|&#x1f472;|&#x1f473;|&#x1f474;|&#x1f475;|&#x1f476;|&#x1f477;|&#x1f478;|&#x1f47c;|&#x1f481;|&#x1f482;|&#x1f483;|&#x1f485;|&#x1f486;|&#x1f487;|&#x1f4aa;|&#x1f57a;|&#x1f595;|&#x1f596;|&#x1f645;|&#x1f646;|&#x1f647;|&#x1f64b;|&#x1f64c;|&#x1f64d;|&#x1f64e;|&#x1f64f;|&#x1f6a3;|&#x1f6b4;|&#x1f6b5;|&#x1f6b6;|&#x1f6c0;|&#x1f6cc;|&#x1f918;|&#x1f919;|&#x1f91a;|&#x1f91b;|&#x1f91c;|&#x1f91e;|&#x1f91f;|&#x1f926;|&#x1f930;|&#x1f931;|&#x1f932;|&#x1f933;|&#x1f934;|&#x1f935;|&#x1f936;|&#x1f937;|&#x1f938;|&#x1f939;|&#x1f93d;|&#x1f93e;|&#x1f9d1;|&#x1f9d2;|&#x1f9d3;|&#x1f9d4;|&#x1f9d5;|&#x1f9d6;|&#x1f9d7;|&#x1f9d8;|&#x1f9d9;|&#x1f9da;|&#x1f9db;|&#x1f9dc;|&#x1f9dd;|(?:&#x270a;|&#x270b;))(?:&#x1f3fb;|&#x1f3fc;|&#x1f3fd;|&#x1f3fe;|&#x1f3ff;|)|&#x1f3f4;&#xe0067;&#xe0062;&#xe0065;&#xe006e;&#xe0067;&#xe007f;|&#x1f3f4;&#xe0067;&#xe0062;&#xe0073;&#xe0063;&#xe0074;&#xe007f;|&#x1f3f4;&#xe0067;&#xe0062;&#xe0077;&#xe006c;&#xe0073;&#xe007f;|&#x1f1e6;&#x1f1e8;|&#x1f1e6;&#x1f1e9;|&#x1f1e6;&#x1f1ea;|&#x1f1e6;&#x1f1eb;|&#x1f1e6;&#x1f1ec;|&#x1f1e6;&#x1f1ee;|&#x1f1e6;&#x1f1f1;|&#x1f1e6;&#x1f1f2;|&#x1f1e6;&#x1f1f4;|&#x1f1e6;&#x1f1f6;|&#x1f1e6;&#x1f1f7;|&#x1f1e6;&#x1f1f8;|&#x1f1e6;&#x1f1f9;|&#x1f1e6;&#x1f1fa;|&#x1f1e6;&#x1f1fc;|&#x1f1e6;&#x1f1fd;|&#x1f1e6;&#x1f1ff;|&#x1f1e7;&#x1f1e6;|&#x1f1e7;&#x1f1e7;|&#x1f1e7;&#x1f1e9;|&#x1f1e7;&#x1f1ea;|&#x1f1e7;&#x1f1eb;|&#x1f1e7;&#x1f1ec;|&#x1f1e7;&#x1f1ed;|&#x1f1e7;&#x1f1ee;|&#x1f1e7;&#x1f1ef;|&#x1f1e7;&#x1f1f1;|&#x1f1e7;&#x1f1f2;|&#x1f1e7;&#x1f1f3;|&#x1f1e7;&#x1f1f4;|&#x1f1e7;&#x1f1f6;|&#x1f1e7;&#x1f1f7;|&#x1f1e7;&#x1f1f8;|&#x1f1e7;&#x1f1f9;|&#x1f1e7;&#x1f1fb;|&#x1f1e7;&#x1f1fc;|&#x1f1e7;&#x1f1fe;|&#x1f1e7;&#x1f1ff;|&#x1f1e8;&#x1f1e6;|&#x1f1e8;&#x1f1e8;|&#x1f1e8;&#x1f1e9;|&#x1f1e8;&#x1f1eb;|&#x1f1e8;&#x1f1ec;|&#x1f1e8;&#x1f1ed;|&#x1f1e8;&#x1f1ee;|&#x1f1e8;&#x1f1f0;|&#x1f1e8;&#x1f1f1;|&#x1f1e8;&#x1f1f2;|&#x1f1e8;&#x1f1f3;|&#x1f1e8;&#x1f1f4;|&#x1f1e8;&#x1f1f5;|&#x1f1e8;&#x1f1f7;|&#x1f1e8;&#x1f1fa;|&#x1f1e8;&#x1f1fb;|&#x1f1e8;&#x1f1fc;|&#x1f1e8;&#x1f1fd;|&#x1f1e8;&#x1f1fe;|&#x1f1e8;&#x1f1ff;|&#x1f1e9;&#x1f1ea;|&#x1f1e9;&#x1f1ec;|&#x1f1e9;&#x1f1ef;|&#x1f1e9;&#x1f1f0;|&#x1f1e9;&#x1f1f2;|&#x1f1e9;&#x1f1f4;|&#x1f1e9;&#x1f1ff;|&#x1f1ea;&#x1f1e6;|&#x1f1ea;&#x1f1e8;|&#x1f1ea;&#x1f1ea;|&#x1f1ea;&#x1f1ec;|&#x1f1ea;&#x1f1ed;|&#x1f1ea;&#x1f1f7;|&#x1f1ea;&#x1f1f8;|&#x1f1ea;&#x1f1f9;|&#x1f1ea;&#x1f1fa;|&#x1f1eb;&#x1f1ee;|&#x1f1eb;&#x1f1ef;|&#x1f1eb;&#x1f1f0;|&#x1f1eb;&#x1f1f2;|&#x1f1eb;&#x1f1f4;|&#x1f1eb;&#x1f1f7;|&#x1f1ec;&#x1f1e6;|&#x1f1ec;&#x1f1e7;|&#x1f1ec;&#x1f1e9;|&#x1f1ec;&#x1f1ea;|&#x1f1ec;&#x1f1eb;|&#x1f1ec;&#x1f1ec;|&#x1f1ec;&#x1f1ed;|&#x1f1ec;&#x1f1ee;|&#x1f1ec;&#x1f1f1;|&#x1f1ec;&#x1f1f2;|&#x1f1ec;&#x1f1f3;|&#x1f1ec;&#x1f1f5;|&#x1f1ec;&#x1f1f6;|&#x1f1ec;&#x1f1f7;|&#x1f1ec;&#x1f1f8;|&#x1f1ec;&#x1f1f9;|&#x1f1ec;&#x1f1fa;|&#x1f1ec;&#x1f1fc;|&#x1f1ec;&#x1f1fe;|&#x1f1ed;&#x1f1f0;|&#x1f1ed;&#x1f1f2;|&#x1f1ed;&#x1f1f3;|&#x1f1ed;&#x1f1f7;|&#x1f1ed;&#x1f1f9;|&#x1f1ed;&#x1f1fa;|&#x1f1ee;&#x1f1e8;|&#x1f1ee;&#x1f1e9;|&#x1f1ee;&#x1f1ea;|&#x1f1ee;&#x1f1f1;|&#x1f1ee;&#x1f1f2;|&#x1f1ee;&#x1f1f3;|&#x1f1ee;&#x1f1f4;|&#x1f1ee;&#x1f1f6;|&#x1f1ee;&#x1f1f7;|&#x1f1ee;&#x1f1f8;|&#x1f1ee;&#x1f1f9;|&#x1f1ef;&#x1f1ea;|&#x1f1ef;&#x1f1f2;|&#x1f1ef;&#x1f1f4;|&#x1f1ef;&#x1f1f5;|&#x1f1f0;&#x1f1ea;|&#x1f1f0;&#x1f1ec;|&#x1f1f0;&#x1f1ed;|&#x1f1f0;&#x1f1ee;|&#x1f1f0;&#x1f1f2;|&#x1f1f0;&#x1f1f3;|&#x1f1f0;&#x1f1f5;|&#x1f1f0;&#x1f1f7;|&#x1f1f0;&#x1f1fc;|&#x1f1f0;&#x1f1fe;|&#x1f1f0;&#x1f1ff;|&#x1f1f1;&#x1f1e6;|&#x1f1f1;&#x1f1e7;|&#x1f1f1;&#x1f1e8;|&#x1f1f1;&#x1f1ee;|&#x1f1f1;&#x1f1f0;|&#x1f1f1;&#x1f1f7;|&#x1f1f1;&#x1f1f8;|&#x1f1f1;&#x1f1f9;|&#x1f1f1;&#x1f1fa;|&#x1f1f1;&#x1f1fb;|&#x1f1f1;&#x1f1fe;|&#x1f1f2;&#x1f1e6;|&#x1f1f2;&#x1f1e8;|&#x1f1f2;&#x1f1e9;|&#x1f1f2;&#x1f1ea;|&#x1f1f2;&#x1f1eb;|&#x1f1f2;&#x1f1ec;|&#x1f1f2;&#x1f1ed;|&#x1f1f2;&#x1f1f0;|&#x1f1f2;&#x1f1f1;|&#x1f1f2;&#x1f1f2;|&#x1f1f2;&#x1f1f3;|&#x1f1f2;&#x1f1f4;|&#x1f1f2;&#x1f1f5;|&#x1f1f2;&#x1f1f6;|&#x1f1f2;&#x1f1f7;|&#x1f1f2;&#x1f1f8;|&#x1f1f2;&#x1f1f9;|&#x1f1f2;&#x1f1fa;|&#x1f1f2;&#x1f1fb;|&#x1f1f2;&#x1f1fc;|&#x1f1f2;&#x1f1fd;|&#x1f1f2;&#x1f1fe;|&#x1f1f2;&#x1f1ff;|&#x1f1f3;&#x1f1e6;|&#x1f1f3;&#x1f1e8;|&#x1f1f3;&#x1f1ea;|&#x1f1f3;&#x1f1eb;|&#x1f1f3;&#x1f1ec;|&#x1f1f3;&#x1f1ee;|&#x1f1f3;&#x1f1f1;|&#x1f1f3;&#x1f1f4;|&#x1f1f3;&#x1f1f5;|&#x1f1f3;&#x1f1f7;|&#x1f1f3;&#x1f1fa;|&#x1f1f3;&#x1f1ff;|&#x1f1f4;&#x1f1f2;|&#x1f1f5;&#x1f1e6;|&#x1f1f5;&#x1f1ea;|&#x1f1f5;&#x1f1eb;|&#x1f1f5;&#x1f1ec;|&#x1f1f5;&#x1f1ed;|&#x1f1f5;&#x1f1f0;|&#x1f1f5;&#x1f1f1;|&#x1f1f5;&#x1f1f2;|&#x1f1f5;&#x1f1f3;|&#x1f1f5;&#x1f1f7;|&#x1f1f5;&#x1f1f8;|&#x1f1f5;&#x1f1f9;|&#x1f1f5;&#x1f1fc;|&#x1f1f5;&#x1f1fe;|&#x1f1f6;&#x1f1e6;|&#x1f1f7;&#x1f1ea;|&#x1f1f7;&#x1f1f4;|&#x1f1f7;&#x1f1f8;|&#x1f1f7;&#x1f1fa;|&#x1f1f7;&#x1f1fc;|&#x1f1f8;&#x1f1e6;|&#x1f1f8;&#x1f1e7;|&#x1f1f8;&#x1f1e8;|&#x1f1f8;&#x1f1e9;|&#x1f1f8;&#x1f1ea;|&#x1f1f8;&#x1f1ec;|&#x1f1f8;&#x1f1ed;|&#x1f1f8;&#x1f1ee;|&#x1f1f8;&#x1f1ef;|&#x1f1f8;&#x1f1f0;|&#x1f1f8;&#x1f1f1;|&#x1f1f8;&#x1f1f2;|&#x1f1f8;&#x1f1f3;|&#x1f1f8;&#x1f1f4;|&#x1f1f8;&#x1f1f7;|&#x1f1f8;&#x1f1f8;|&#x1f1f8;&#x1f1f9;|&#x1f1f8;&#x1f1fb;|&#x1f1f8;&#x1f1fd;|&#x1f1f8;&#x1f1fe;|&#x1f1f8;&#x1f1ff;|&#x1f1f9;&#x1f1e6;|&#x1f1f9;&#x1f1e8;|&#x1f1f9;&#x1f1e9;|&#x1f1f9;&#x1f1eb;|&#x1f1f9;&#x1f1ec;|&#x1f1f9;&#x1f1ed;|&#x1f1f9;&#x1f1ef;|&#x1f1f9;&#x1f1f0;|&#x1f1f9;&#x1f1f1;|&#x1f1f9;&#x1f1f2;|&#x1f1f9;&#x1f1f3;|&#x1f1f9;&#x1f1f4;|&#x1f1f9;&#x1f1f7;|&#x1f1f9;&#x1f1f9;|&#x1f1f9;&#x1f1fb;|&#x1f1f9;&#x1f1fc;|&#x1f1f9;&#x1f1ff;|&#x1f1fa;&#x1f1e6;|&#x1f1fa;&#x1f1ec;|&#x1f1fa;&#x1f1f2;|&#x1f1fa;&#x1f1f3;|&#x1f1fa;&#x1f1f8;|&#x1f1fa;&#x1f1fe;|&#x1f1fa;&#x1f1ff;|&#x1f1fb;&#x1f1e6;|&#x1f1fb;&#x1f1e8;|&#x1f1fb;&#x1f1ea;|&#x1f1fb;&#x1f1ec;|&#x1f1fb;&#x1f1ee;|&#x1f1fb;&#x1f1f3;|&#x1f1fb;&#x1f1fa;|&#x1f1fc;&#x1f1eb;|&#x1f1fc;&#x1f1f8;|&#x1f1fd;&#x1f1f0;|&#x1f1fe;&#x1f1ea;|&#x1f1fe;&#x1f1f9;|&#x1f1ff;&#x1f1e6;|&#x1f1ff;&#x1f1f2;|&#x1f1ff;&#x1f1fc;|&#x10000;|&#x1f0cf;|&#x1f18e;|&#x1f191;|&#x1f192;|&#x1f193;|&#x1f194;|&#x1f195;|&#x1f196;|&#x1f197;|&#x1f198;|&#x1f199;|&#x1f19a;|&#x1f1e6;|&#x1f1e7;|&#x1f1e8;|&#x1f1e9;|&#x1f1ea;|&#x1f1eb;|&#x1f1ec;|&#x1f1ed;|&#x1f1ee;|&#x1f1ef;|&#x1f1f0;|&#x1f1f1;|&#x1f1f2;|&#x1f1f3;|&#x1f1f4;|&#x1f1f5;|&#x1f1f6;|&#x1f1f7;|&#x1f1f8;|&#x1f1f9;|&#x1f1fa;|&#x1f1fb;|&#x1f1fc;|&#x1f1fd;|&#x1f1fe;|&#x1f1ff;|&#x1f201;|&#x1f232;|&#x1f233;|&#x1f234;|&#x1f235;|&#x1f236;|&#x1f238;|&#x1f239;|&#x1f23a;|&#x1f250;|&#x1f251;|&#x1f300;|&#x1f301;|&#x1f302;|&#x1f303;|&#x1f304;|&#x1f305;|&#x1f306;|&#x1f307;|&#x1f308;|&#x1f309;|&#x1f30a;|&#x1f30b;|&#x1f30c;|&#x1f30d;|&#x1f30e;|&#x1f30f;|&#x1f310;|&#x1f311;|&#x1f312;|&#x1f313;|&#x1f314;|&#x1f315;|&#x1f316;|&#x1f317;|&#x1f318;|&#x1f319;|&#x1f31a;|&#x1f31b;|&#x1f31c;|&#x1f31d;|&#x1f31e;|&#x1f31f;|&#x1f320;|&#x1f32d;|&#x1f32e;|&#x1f32f;|&#x1f330;|&#x1f331;|&#x1f332;|&#x1f333;|&#x1f334;|&#x1f335;|&#x1f337;|&#x1f338;|&#x1f339;|&#x1f33a;|&#x1f33b;|&#x1f33c;|&#x1f33d;|&#x1f33e;|&#x1f33f;|&#x1f340;|&#x1f341;|&#x1f342;|&#x1f343;|&#x1f344;|&#x1f345;|&#x1f346;|&#x1f347;|&#x1f348;|&#x1f349;|&#x1f34a;|&#x1f34b;|&#x1f34c;|&#x1f34d;|&#x1f34e;|&#x1f34f;|&#x1f350;|&#x1f351;|&#x1f352;|&#x1f353;|&#x1f354;|&#x1f355;|&#x1f356;|&#x1f357;|&#x1f358;|&#x1f359;|&#x1f35a;|&#x1f35b;|&#x1f35c;|&#x1f35d;|&#x1f35e;|&#x1f35f;|&#x1f360;|&#x1f361;|&#x1f362;|&#x1f363;|&#x1f364;|&#x1f365;|&#x1f366;|&#x1f367;|&#x1f368;|&#x1f369;|&#x1f36a;|&#x1f36b;|&#x1f36c;|&#x1f36d;|&#x1f36e;|&#x1f36f;|&#x1f370;|&#x1f371;|&#x1f372;|&#x1f373;|&#x1f374;|&#x1f375;|&#x1f376;|&#x1f377;|&#x1f378;|&#x1f379;|&#x1f37a;|&#x1f37b;|&#x1f37c;|&#x1f37e;|&#x1f37f;|&#x1f380;|&#x1f381;|&#x1f382;|&#x1f383;|&#x1f384;|&#x1f386;|&#x1f387;|&#x1f388;|&#x1f389;|&#x1f38a;|&#x1f38b;|&#x1f38c;|&#x1f38d;|&#x1f38e;|&#x1f38f;|&#x1f390;|&#x1f391;|&#x1f392;|&#x1f393;|&#x1f3a0;|&#x1f3a1;|&#x1f3a2;|&#x1f3a3;|&#x1f3a4;|&#x1f3a5;|&#x1f3a6;|&#x1f3a7;|&#x1f3a8;|&#x1f3a9;|&#x1f3aa;|&#x1f3ab;|&#x1f3ac;|&#x1f3ad;|&#x1f3ae;|&#x1f3af;|&#x1f3b0;|&#x1f3b1;|&#x1f3b2;|&#x1f3b3;|&#x1f3b4;|&#x1f3b5;|&#x1f3b6;|&#x1f3b7;|&#x1f3b8;|&#x1f3b9;|&#x1f3ba;|&#x1f3bb;|&#x1f3bc;|&#x1f3bd;|&#x1f3be;|&#x1f3bf;|&#x1f3c0;|&#x1f3c1;|&#x1f3c5;|&#x1f3c6;|&#x1f3c8;|&#x1f3c9;|&#x1f3cf;|&#x1f3d0;|&#x1f3d1;|&#x1f3d2;|&#x1f3d3;|&#x1f3e0;|&#x1f3e1;|&#x1f3e2;|&#x1f3e3;|&#x1f3e4;|&#x1f3e5;|&#x1f3e6;|&#x1f3e7;|&#x1f3e8;|&#x1f3e9;|&#x1f3ea;|&#x1f3eb;|&#x1f3ec;|&#x1f3ed;|&#x1f3ee;|&#x1f3ef;|&#x1f3f0;|&#x1f3f4;|&#x1f3f8;|&#x1f3f9;|&#x1f3fa;|&#x1f3fb;|&#x1f3fc;|&#x1f3fd;|&#x1f3fe;|&#x1f3ff;|&#x1f400;|&#x1f401;|&#x1f402;|&#x1f403;|&#x1f404;|&#x1f405;|&#x1f406;|&#x1f407;|&#x1f408;|&#x1f409;|&#x1f40a;|&#x1f40b;|&#x1f40c;|&#x1f40d;|&#x1f40e;|&#x1f40f;|&#x1f410;|&#x1f411;|&#x1f412;|&#x1f413;|&#x1f414;|&#x1f415;|&#x1f416;|&#x1f417;|&#x1f418;|&#x1f419;|&#x1f41a;|&#x1f41b;|&#x1f41c;|&#x1f41d;|&#x1f41e;|&#x1f41f;|&#x1f420;|&#x1f421;|&#x1f422;|&#x1f423;|&#x1f424;|&#x1f425;|&#x1f426;|&#x1f427;|&#x1f428;|&#x1f429;|&#x1f42a;|&#x1f42b;|&#x1f42c;|&#x1f42d;|&#x1f42e;|&#x1f42f;|&#x1f430;|&#x1f431;|&#x1f432;|&#x1f433;|&#x1f434;|&#x1f435;|&#x1f436;|&#x1f437;|&#x1f438;|&#x1f439;|&#x1f43a;|&#x1f43b;|&#x1f43c;|&#x1f43d;|&#x1f43e;|&#x1f440;|&#x1f444;|&#x1f445;|&#x1f451;|&#x1f452;|&#x1f453;|&#x1f454;|&#x1f455;|&#x1f456;|&#x1f457;|&#x1f458;|&#x1f459;|&#x1f45a;|&#x1f45b;|&#x1f45c;|&#x1f45d;|&#x1f45e;|&#x1f45f;|&#x1f460;|&#x1f461;|&#x1f462;|&#x1f463;|&#x1f464;|&#x1f465;|&#x1f46a;|&#x1f46b;|&#x1f46c;|&#x1f46d;|&#x1f46f;|&#x1f479;|&#x1f47a;|&#x1f47b;|&#x1f47d;|&#x1f47e;|&#x1f47f;|&#x1f480;|&#x1f484;|&#x1f488;|&#x1f489;|&#x1f48a;|&#x1f48b;|&#x1f48c;|&#x1f48d;|&#x1f48e;|&#x1f48f;|&#x1f490;|&#x1f491;|&#x1f492;|&#x1f493;|&#x1f494;|&#x1f495;|&#x1f496;|&#x1f497;|&#x1f498;|&#x1f499;|&#x1f49a;|&#x1f49b;|&#x1f49c;|&#x1f49d;|&#x1f49e;|&#x1f49f;|&#x1f4a0;|&#x1f4a1;|&#x1f4a2;|&#x1f4a3;|&#x1f4a4;|&#x1f4a5;|&#x1f4a6;|&#x1f4a7;|&#x1f4a8;|&#x1f4a9;|&#x1f4ab;|&#x1f4ac;|&#x1f4ad;|&#x1f4ae;|&#x1f4af;|&#x1f4b0;|&#x1f4b1;|&#x1f4b2;|&#x1f4b3;|&#x1f4b4;|&#x1f4b5;|&#x1f4b6;|&#x1f4b7;|&#x1f4b8;|&#x1f4b9;|&#x1f4ba;|&#x1f4bb;|&#x1f4bc;|&#x1f4bd;|&#x1f4be;|&#x1f4bf;|&#x1f4c0;|&#x1f4c1;|&#x1f4c2;|&#x1f4c3;|&#x1f4c4;|&#x1f4c5;|&#x1f4c6;|&#x1f4c7;|&#x1f4c8;|&#x1f4c9;|&#x1f4ca;|&#x1f4cb;|&#x1f4cc;|&#x1f4cd;|&#x1f4ce;|&#x1f4cf;|&#x1f4d0;|&#x1f4d1;|&#x1f4d2;|&#x1f4d3;|&#x1f4d4;|&#x1f4d5;|&#x1f4d6;|&#x1f4d7;|&#x1f4d8;|&#x1f4d9;|&#x1f4da;|&#x1f4db;|&#x1f4dc;|&#x1f4dd;|&#x1f4de;|&#x1f4df;|&#x1f4e0;|&#x1f4e1;|&#x1f4e2;|&#x1f4e3;|&#x1f4e4;|&#x1f4e5;|&#x1f4e6;|&#x1f4e7;|&#x1f4e8;|&#x1f4e9;|&#x1f4ea;|&#x1f4eb;|&#x1f4ec;|&#x1f4ed;|&#x1f4ee;|&#x1f4ef;|&#x1f4f0;|&#x1f4f1;|&#x1f4f2;|&#x1f4f3;|&#x1f4f4;|&#x1f4f5;|&#x1f4f6;|&#x1f4f7;|&#x1f4f8;|&#x1f4f9;|&#x1f4fa;|&#x1f4fb;|&#x1f4fc;|&#x1f4ff;|&#x1f500;|&#x1f501;|&#x1f502;|&#x1f503;|&#x1f504;|&#x1f505;|&#x1f506;|&#x1f507;|&#x1f508;|&#x1f509;|&#x1f50a;|&#x1f50b;|&#x1f50c;|&#x1f50d;|&#x1f50e;|&#x1f50f;|&#x1f510;|&#x1f511;|&#x1f512;|&#x1f513;|&#x1f514;|&#x1f515;|&#x1f516;|&#x1f517;|&#x1f518;|&#x1f519;|&#x1f51a;|&#x1f51b;|&#x1f51c;|&#x1f51d;|&#x1f51e;|&#x1f51f;|&#x1f520;|&#x1f521;|&#x1f522;|&#x1f523;|&#x1f524;|&#x1f525;|&#x1f526;|&#x1f527;|&#x1f528;|&#x1f529;|&#x1f52a;|&#x1f52b;|&#x1f52c;|&#x1f52d;|&#x1f52e;|&#x1f52f;|&#x1f530;|&#x1f531;|&#x1f532;|&#x1f533;|&#x1f534;|&#x1f535;|&#x1f536;|&#x1f537;|&#x1f538;|&#x1f539;|&#x1f53a;|&#x1f53b;|&#x1f53c;|&#x1f53d;|&#x1f54b;|&#x1f54c;|&#x1f54d;|&#x1f54e;|&#x1f550;|&#x1f551;|&#x1f552;|&#x1f553;|&#x1f554;|&#x1f555;|&#x1f556;|&#x1f557;|&#x1f558;|&#x1f559;|&#x1f55a;|&#x1f55b;|&#x1f55c;|&#x1f55d;|&#x1f55e;|&#x1f55f;|&#x1f560;|&#x1f561;|&#x1f562;|&#x1f563;|&#x1f564;|&#x1f565;|&#x1f566;|&#x1f567;|&#x1f5a4;|&#x1f5fb;|&#x1f5fc;|&#x1f5fd;|&#x1f5fe;|&#x1f5ff;|&#x1f600;|&#x1f601;|&#x1f602;|&#x1f603;|&#x1f604;|&#x1f605;|&#x1f606;|&#x1f607;|&#x1f608;|&#x1f609;|&#x1f60a;|&#x1f60b;|&#x1f60c;|&#x1f60d;|&#x1f60e;|&#x1f60f;|&#x1f610;|&#x1f611;|&#x1f612;|&#x1f613;|&#x1f614;|&#x1f615;|&#x1f616;|&#x1f617;|&#x1f618;|&#x1f619;|&#x1f61a;|&#x1f61b;|&#x1f61c;|&#x1f61d;|&#x1f61e;|&#x1f61f;|&#x1f620;|&#x1f621;|&#x1f622;|&#x1f623;|&#x1f624;|&#x1f625;|&#x1f626;|&#x1f627;|&#x1f628;|&#x1f629;|&#x1f62a;|&#x1f62b;|&#x1f62c;|&#x1f62d;|&#x1f62e;|&#x1f62f;|&#x1f630;|&#x1f631;|&#x1f632;|&#x1f633;|&#x1f634;|&#x1f635;|&#x1f636;|&#x1f637;|&#x1f638;|&#x1f639;|&#x1f63a;|&#x1f63b;|&#x1f63c;|&#x1f63d;|&#x1f63e;|&#x1f63f;|&#x1f640;|&#x1f641;|&#x1f642;|&#x1f643;|&#x1f644;|&#x1f648;|&#x1f649;|&#x1f64a;|&#x1f680;|&#x1f681;|&#x1f682;|&#x1f683;|&#x1f684;|&#x1f685;|&#x1f686;|&#x1f687;|&#x1f688;|&#x1f689;|&#x1f68a;|&#x1f68b;|&#x1f68c;|&#x1f68d;|&#x1f68e;|&#x1f68f;|&#x1f690;|&#x1f691;|&#x1f692;|&#x1f693;|&#x1f694;|&#x1f695;|&#x1f696;|&#x1f697;|&#x1f698;|&#x1f699;|&#x1f69a;|&#x1f69b;|&#x1f69c;|&#x1f69d;|&#x1f69e;|&#x1f69f;|&#x1f6a0;|&#x1f6a1;|&#x1f6a2;|&#x1f6a4;|&#x1f6a5;|&#x1f6a6;|&#x1f6a7;|&#x1f6a8;|&#x1f6a9;|&#x1f6aa;|&#x1f6ab;|&#x1f6ac;|&#x1f6ad;|&#x1f6ae;|&#x1f6af;|&#x1f6b0;|&#x1f6b1;|&#x1f6b2;|&#x1f6b3;|&#x1f6b7;|&#x1f6b8;|&#x1f6b9;|&#x1f6ba;|&#x1f6bb;|&#x1f6bc;|&#x1f6bd;|&#x1f6be;|&#x1f6bf;|&#x1f6c1;|&#x1f6c2;|&#x1f6c3;|&#x1f6c4;|&#x1f6c5;|&#x1f6d0;|&#x1f6d1;|&#x1f6d2;|&#x1f6eb;|&#x1f6ec;|&#x1f6f4;|&#x1f6f5;|&#x1f6f6;|&#x1f6f7;|&#x1f6f8;|&#x1f910;|&#x1f911;|&#x1f912;|&#x1f913;|&#x1f914;|&#x1f915;|&#x1f916;|&#x1f917;|&#x1f91d;|&#x1f920;|&#x1f921;|&#x1f922;|&#x1f923;|&#x1f924;|&#x1f925;|&#x1f927;|&#x1f928;|&#x1f929;|&#x1f92a;|&#x1f92b;|&#x1f92c;|&#x1f92d;|&#x1f92e;|&#x1f92f;|&#x1f93a;|&#x1f93c;|&#x1f940;|&#x1f941;|&#x1f942;|&#x1f943;|&#x1f944;|&#x1f945;|&#x1f947;|&#x1f948;|&#x1f949;|&#x1f94a;|&#x1f94b;|&#x1f94c;|&#x1f950;|&#x1f951;|&#x1f952;|&#x1f953;|&#x1f954;|&#x1f955;|&#x1f956;|&#x1f957;|&#x1f958;|&#x1f959;|&#x1f95a;|&#x1f95b;|&#x1f95c;|&#x1f95d;|&#x1f95e;|&#x1f95f;|&#x1f960;|&#x1f961;|&#x1f962;|&#x1f963;|&#x1f964;|&#x1f965;|&#x1f966;|&#x1f967;|&#x1f968;|&#x1f969;|&#x1f96a;|&#x1f96b;|&#x1f980;|&#x1f981;|&#x1f982;|&#x1f983;|&#x1f984;|&#x1f985;|&#x1f986;|&#x1f987;|&#x1f988;|&#x1f989;|&#x1f98a;|&#x1f98b;|&#x1f98c;|&#x1f98d;|&#x1f98e;|&#x1f98f;|&#x1f990;|&#x1f991;|&#x1f992;|&#x1f993;|&#x1f994;|&#x1f995;|&#x1f996;|&#x1f997;|&#x1f9c0;|&#x1f9d0;|&#x1f9de;|&#x1f9df;|&#x1f9e0;|&#x1f9e1;|&#x1f9e2;|&#x1f9e3;|&#x1f9e4;|&#x1f9e5;|&#x1f9e6;|(?:&#x23e9;|&#x23ea;|&#x23eb;|&#x23ec;|&#x23f0;|&#x23f3;|&#x2640;|&#x2642;|&#x2695;|&#x26ce;|&#x2705;|&#x2728;|&#x274c;|&#x274e;|&#x2753;|&#x2754;|&#x2755;|&#x2795;|&#x2796;|&#x2797;|&#x27b0;|&#x27bf;|&#xe50a;)|(?:&#x1f004;|&#x1f170;|&#x1f171;|&#x1f17e;|&#x1f17f;|&#x1f202;|&#x1f21a;|&#x1f22f;|&#x1f237;|&#x1f321;|&#x1f324;|&#x1f325;|&#x1f326;|&#x1f327;|&#x1f328;|&#x1f329;|&#x1f32a;|&#x1f32b;|&#x1f32c;|&#x1f336;|&#x1f37d;|&#x1f396;|&#x1f397;|&#x1f399;|&#x1f39a;|&#x1f39b;|&#x1f39e;|&#x1f39f;|&#x1f3cd;|&#x1f3ce;|&#x1f3d4;|&#x1f3d5;|&#x1f3d6;|&#x1f3d7;|&#x1f3d8;|&#x1f3d9;|&#x1f3da;|&#x1f3db;|&#x1f3dc;|&#x1f3dd;|&#x1f3de;|&#x1f3df;|&#x1f3f3;|&#x1f3f5;|&#x1f3f7;|&#x1f43f;|&#x1f441;|&#x1f4fd;|&#x1f549;|&#x1f54a;|&#x1f56f;|&#x1f570;|&#x1f573;|&#x1f576;|&#x1f577;|&#x1f578;|&#x1f579;|&#x1f587;|&#x1f58a;|&#x1f58b;|&#x1f58c;|&#x1f58d;|&#x1f5a5;|&#x1f5a8;|&#x1f5b1;|&#x1f5b2;|&#x1f5bc;|&#x1f5c2;|&#x1f5c3;|&#x1f5c4;|&#x1f5d1;|&#x1f5d2;|&#x1f5d3;|&#x1f5dc;|&#x1f5dd;|&#x1f5de;|&#x1f5e1;|&#x1f5e3;|&#x1f5e8;|&#x1f5ef;|&#x1f5f3;|&#x1f5fa;|&#x1f6cb;|&#x1f6cd;|&#x1f6ce;|&#x1f6cf;|&#x1f6e0;|&#x1f6e1;|&#x1f6e2;|&#x1f6e3;|&#x1f6e4;|&#x1f6e5;|&#x1f6e9;|&#x1f6f0;|&#x1f6f3;|(?:&#x00a9;|&#x00ae;|&#x203c;|&#x2049;|&#x2122;|&#x2139;|&#x2194;|&#x2195;|&#x2196;|&#x2197;|&#x2198;|&#x2199;|&#x21a9;|&#x21aa;|&#x231a;|&#x231b;|&#x2328;|&#x23cf;|&#x23ed;|&#x23ee;|&#x23ef;|&#x23f1;|&#x23f2;|&#x23f8;|&#x23f9;|&#x23fa;|&#x24c2;|&#x25aa;|&#x25ab;|&#x25b6;|&#x25c0;|&#x25fb;|&#x25fc;|&#x25fd;|&#x25fe;|&#x2600;|&#x2601;|&#x2602;|&#x2603;|&#x2604;|&#x260e;|&#x2611;|&#x2614;|&#x2615;|&#x2618;|&#x2620;|&#x2622;|&#x2623;|&#x2626;|&#x262a;|&#x262e;|&#x262f;|&#x2638;|&#x2639;|&#x263a;|&#x2648;|&#x2649;|&#x264a;|&#x264b;|&#x264c;|&#x264d;|&#x264e;|&#x264f;|&#x2650;|&#x2651;|&#x2652;|&#x2653;|&#x2660;|&#x2663;|&#x2665;|&#x2666;|&#x2668;|&#x267b;|&#x267f;|&#x2692;|&#x2693;|&#x2694;|&#x2696;|&#x2697;|&#x2699;|&#x269b;|&#x269c;|&#x26a0;|&#x26a1;|&#x26aa;|&#x26ab;|&#x26b0;|&#x26b1;|&#x26bd;|&#x26be;|&#x26c4;|&#x26c5;|&#x26c8;|&#x26cf;|&#x26d1;|&#x26d3;|&#x26d4;|&#x26e9;|&#x26ea;|&#x26f0;|&#x26f1;|&#x26f2;|&#x26f3;|&#x26f4;|&#x26f5;|&#x26f8;|&#x26fa;|&#x26fd;|&#x2702;|&#x2708;|&#x2709;|&#x270f;|&#x2712;|&#x2714;|&#x2716;|&#x271d;|&#x2721;|&#x2733;|&#x2734;|&#x2744;|&#x2747;|&#x2757;|&#x2763;|&#x2764;|&#x27a1;|&#x2934;|&#x2935;|&#x2b05;|&#x2b06;|&#x2b07;|&#x2b1b;|&#x2b1c;|&#x2b50;|&#x2b55;|&#x3030;|&#x303d;|&#x3297;|&#x3299;))(?:&#xfe0f;|(?!&#xfe0e;)))/u';
5325        // END: emoji regex
5326
5327        if ( 'entities' === $type ) {
5328                return $entities;
5329        }
5330
5331        return $codepoints;
5332}
5333
5334/**
5335 * Shorten a URL, to be used as link text.
5336 *
5337 * @since 1.2.0
5338 * @since 4.4.0 Moved to wp-includes/formatting.php from wp-admin/includes/misc.php and added $length param.
5339 *
5340 * @param string $url    URL to shorten.
5341 * @param int    $length Optional. Maximum length of the shortened URL. Default 35 characters.
5342 * @return string Shortened URL.
5343 */
5344function url_shorten( $url, $length = 35 ) {
5345        $stripped = str_replace( array( 'https://', 'http://', 'www.' ), '', $url );
5346        $short_url = untrailingslashit( $stripped );
5347
5348        if ( strlen( $short_url ) > $length ) {
5349                $short_url = substr( $short_url, 0, $length - 3 ) . '&hellip;';
5350        }
5351        return $short_url;
5352}
5353
5354/**
5355 * Sanitizes a hex color.
5356 *
5357 * Returns either '', a 3 or 6 digit hex color (with #), or nothing.
5358 * For sanitizing values without a #, see sanitize_hex_color_no_hash().
5359 *
5360 * @since 3.4.0
5361 *
5362 * @param string $color
5363 * @return string|void
5364 */
5365function sanitize_hex_color( $color ) {
5366        if ( '' === $color ) {
5367                return '';
5368        }
5369
5370        // 3 or 6 hex digits, or the empty string.
5371        if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
5372                return $color;
5373        }
5374}
5375
5376/**
5377 * Sanitizes a hex color without a hash. Use sanitize_hex_color() when possible.
5378 *
5379 * Saving hex colors without a hash puts the burden of adding the hash on the
5380 * UI, which makes it difficult to use or upgrade to other color types such as
5381 * rgba, hsl, rgb, and html color names.
5382 *
5383 * Returns either '', a 3 or 6 digit hex color (without a #), or null.
5384 *
5385 * @since 3.4.0
5386 *
5387 * @param string $color
5388 * @return string|null
5389 */
5390function sanitize_hex_color_no_hash( $color ) {
5391        $color = ltrim( $color, '#' );
5392
5393        if ( '' === $color ) {
5394                return '';
5395        }
5396
5397        return sanitize_hex_color( '#' . $color ) ? $color : null;
5398}
5399
5400/**
5401 * Ensures that any hex color is properly hashed.
5402 * Otherwise, returns value untouched.
5403 *
5404 * This method should only be necessary if using sanitize_hex_color_no_hash().
5405 *
5406 * @since 3.4.0
5407 *
5408 * @param string $color
5409 * @return string
5410 */
5411function maybe_hash_hex_color( $color ) {
5412        if ( $unhashed = sanitize_hex_color_no_hash( $color ) ) {
5413                return '#' . $unhashed;
5414        }
5415
5416        return $color;
5417}
5418
5419/**
5420 * Sanitizes a checkbox from user input or from the database.
5421 *
5422 * @see sanitize_checkbox()
5423 *
5424 * @param bool $checked Checkbox to sanitize.
5425 * @return bool Sanitized checkbox, true or false.
5426 */
5427function sanitize_checkbox( $checked ) {
5428        // Boolean check.
5429        $filtered = ( isset( $checked ) && true == $checked ) ? true : false;
5430
5431        /**
5432         * Filters a sanitized checkbox.
5433         *
5434         * @param bool $filtered The sanitized checkbox.
5435         * @param bool $checked  The chekbox prior to being sanitized.
5436         */
5437         return apply_filters( 'sanitize_checkbox', $filtered, $checked );
5438}