WPTexturize.php 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113
  1. <?php
  2. /**
  3. * @group formatting
  4. */
  5. class Tests_Formatting_WPTexturize extends WP_UnitTestCase {
  6. function test_dashes() {
  7. $this->assertSame( 'Hey &#8212; boo?', wptexturize( 'Hey -- boo?' ) );
  8. $this->assertSame( '<a href="http://xx--xx">Hey &#8212; boo?</a>', wptexturize( '<a href="http://xx--xx">Hey -- boo?</a>' ) );
  9. }
  10. function test_disable() {
  11. $this->assertSame( '<pre>---&</pre>', wptexturize( '<pre>---&</pre>' ) );
  12. $this->assertSame( '<pre><code></code>--&</pre>', wptexturize( '<pre><code></code>--&</pre>' ) );
  13. $this->assertSame( '<code>---&</code>', wptexturize( '<code>---&</code>' ) );
  14. $this->assertSame( '<kbd>---&</kbd>', wptexturize( '<kbd>---&</kbd>' ) );
  15. $this->assertSame( '<style>---&</style>', wptexturize( '<style>---&</style>' ) );
  16. $this->assertSame( '<script>---&</script>', wptexturize( '<script>---&</script>' ) );
  17. $this->assertSame( '<tt>---&</tt>', wptexturize( '<tt>---&</tt>' ) );
  18. $this->assertSame( '<code>href="baba"</code> &#8220;baba&#8221;', wptexturize( '<code>href="baba"</code> "baba"' ) );
  19. $enabled_tags_inside_code = '<code>curl -s <a href="http://x/">baba</a> | grep sfive | cut -d "\"" -f 10 &gt; topmp3.txt</code>';
  20. $this->assertSame( $enabled_tags_inside_code, wptexturize( $enabled_tags_inside_code ) );
  21. $double_nest = '<pre>"baba"<code>"baba"<pre></pre></code>"baba"</pre>';
  22. $this->assertSame( $double_nest, wptexturize( $double_nest ) );
  23. $invalid_nest = '<pre></code>"baba"</pre>';
  24. $this->assertSame( $invalid_nest, wptexturize( $invalid_nest ) );
  25. }
  26. /**
  27. * @ticket 1418
  28. */
  29. function test_bracketed_quotes_1418() {
  30. $this->assertSame( '(&#8220;test&#8221;)', wptexturize( '("test")' ) );
  31. $this->assertSame( '(&#8216;test&#8217;)', wptexturize( "('test')" ) );
  32. $this->assertSame( '(&#8217;twas)', wptexturize( "('twas)" ) );
  33. }
  34. /**
  35. * @ticket 3810
  36. */
  37. function test_bracketed_quotes_3810() {
  38. $this->assertSame( 'A dog (&#8220;Hubertus&#8221;) was sent out.', wptexturize( 'A dog ("Hubertus") was sent out.' ) );
  39. }
  40. /**
  41. * @ticket 4539
  42. */
  43. function test_basic_quotes() {
  44. $this->assertSame( 'test&#8217;s', wptexturize( 'test\'s' ) );
  45. $this->assertSame( '&#8216;quoted&#8217;', wptexturize( '\'quoted\'' ) );
  46. $this->assertSame( '&#8220;quoted&#8221;', wptexturize( '"quoted"' ) );
  47. $this->assertSame( 'space before &#8216;quoted&#8217; space after', wptexturize( 'space before \'quoted\' space after' ) );
  48. $this->assertSame( 'space before &#8220;quoted&#8221; space after', wptexturize( 'space before "quoted" space after' ) );
  49. $this->assertSame( '(&#8216;quoted&#8217;)', wptexturize( '(\'quoted\')' ) );
  50. $this->assertSame( '{&#8220;quoted&#8221;}', wptexturize( '{"quoted"}' ) );
  51. $this->assertSame( '&#8216;qu(ot)ed&#8217;', wptexturize( '\'qu(ot)ed\'' ) );
  52. $this->assertSame( '&#8220;qu{ot}ed&#8221;', wptexturize( '"qu{ot}ed"' ) );
  53. $this->assertSame( ' &#8216;test&#8217;s quoted&#8217; ', wptexturize( ' \'test\'s quoted\' ' ) );
  54. $this->assertSame( ' &#8220;test&#8217;s quoted&#8221; ', wptexturize( ' "test\'s quoted" ' ) );
  55. }
  56. /**
  57. * @ticket 4539
  58. * @ticket 15241
  59. */
  60. function test_full_sentences_with_unmatched_single_quotes() {
  61. $this->assertSame(
  62. 'That means every moment you&#8217;re working on something without it being in the public it&#8217;s actually dying.',
  63. wptexturize( "That means every moment you're working on something without it being in the public it's actually dying." )
  64. );
  65. }
  66. /**
  67. * @ticket 4539
  68. */
  69. function test_quotes() {
  70. $this->assertSame( '&#8220;Quoted String&#8221;', wptexturize( '"Quoted String"' ) );
  71. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>"' ) );
  72. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link and a period</a>&#8221;.', wptexturize( 'Here is "<a href="http://example.com">a test with a link and a period</a>".' ) );
  73. $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221; and a space.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>" and a space.' ) );
  74. $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a> and some text quoted&#8221;', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a> and some text quoted"' ) );
  75. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;, and a comma.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>", and a comma.' ) );
  76. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;; and a semi-colon.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>"; and a semi-colon.' ) );
  77. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;- and a dash.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>"- and a dash.' ) );
  78. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;&#8230; and ellipses.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>"... and ellipses.' ) );
  79. // $this->assertSame( 'Here is &#8220;a test <a href="http://example.com">with a link</a>&#8221;.', wptexturize( 'Here is "a test <a href="http://example.com">with a link</a>".' ) );
  80. // $this->assertSame( 'Here is &#8220;<a href="http://example.com">a test with a link</a>&#8221;and a work stuck to the end.', wptexturize( 'Here is "<a href="http://example.com">a test with a link</a>"and a work stuck to the end.' ) );
  81. $this->assertSame( 'A test with a finishing number, &#8220;like 23&#8221;.', wptexturize( 'A test with a finishing number, "like 23".' ) );
  82. $this->assertSame( 'A test with a number, &#8220;like 62&#8221;, is nice to have.', wptexturize( 'A test with a number, "like 62", is nice to have.' ) );
  83. }
  84. /**
  85. * @ticket 4539
  86. */
  87. function test_quotes_before_s() {
  88. $this->assertSame( 'test&#8217;s', wptexturize( "test's" ) );
  89. $this->assertSame( '&#8216;test&#8217;s', wptexturize( "'test's" ) );
  90. $this->assertSame( '&#8216;test&#8217;s&#8217;', wptexturize( "'test's'" ) );
  91. $this->assertSame( '&#8216;string&#8217;', wptexturize( "'string'" ) );
  92. $this->assertSame( '&#8216;string&#8217;s&#8217;', wptexturize( "'string's'" ) );
  93. }
  94. /**
  95. * @ticket 4539
  96. */
  97. function test_quotes_before_numbers() {
  98. $this->assertSame( 'Class of &#8217;99', wptexturize( "Class of '99" ) );
  99. $this->assertSame( 'Class of &#8217;99&#8217;s', wptexturize( "Class of '99's" ) );
  100. $this->assertSame( '&#8216;Class of &#8217;99&#8217;', wptexturize( "'Class of '99'" ) );
  101. $this->assertSame( '&#8216;Class of &#8217;99&#8217; ', wptexturize( "'Class of '99' " ) );
  102. $this->assertSame( '&#8216;Class of &#8217;99&#8217;.', wptexturize( "'Class of '99'." ) );
  103. $this->assertSame( '&#8216;Class of &#8217;99&#8217;, she said', wptexturize( "'Class of '99', she said" ) );
  104. $this->assertSame( '&#8216;Class of &#8217;99&#8217;:', wptexturize( "'Class of '99':" ) );
  105. $this->assertSame( '&#8216;Class of &#8217;99&#8217;;', wptexturize( "'Class of '99';" ) );
  106. $this->assertSame( '&#8216;Class of &#8217;99&#8217;!', wptexturize( "'Class of '99'!" ) );
  107. $this->assertSame( '&#8216;Class of &#8217;99&#8217;?', wptexturize( "'Class of '99'?" ) );
  108. $this->assertSame( '&#8216;Class of &#8217;99&#8217;s&#8217;', wptexturize( "'Class of '99's'" ) );
  109. $this->assertSame( '&#8216;Class of &#8217;99&#8217;s&#8217;', wptexturize( "'Class of '99&#8217;s'" ) );
  110. $this->assertSame( '&#8220;Class of 99&#8221;', wptexturize( '"Class of 99"' ) );
  111. $this->assertSame( '&#8220;Class of &#8217;99&#8221;', wptexturize( "\"Class of '99\"" ) );
  112. $this->assertSame( '{&#8220;Class of &#8217;99&#8221;}', wptexturize( "{\"Class of '99\"}" ) );
  113. $this->assertSame( ' &#8220;Class of &#8217;99&#8221; ', wptexturize( " \"Class of '99\" " ) );
  114. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;.', wptexturize( " \"Class of '99\"." ) );
  115. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;, she said', wptexturize( " \"Class of '99\", she said" ) );
  116. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;:', wptexturize( " \"Class of '99\":" ) );
  117. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;;', wptexturize( " \"Class of '99\";" ) );
  118. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;!', wptexturize( " \"Class of '99\"!" ) );
  119. $this->assertSame( ' &#8220;Class of &#8217;99&#8221;?', wptexturize( " \"Class of '99\"?" ) );
  120. // Not a quotation, may be between two other quotations.
  121. $this->assertSame( '}&#8221;Class of &#8217;99&#8243;{', wptexturize( "}\"Class of '99\"{" ) );
  122. }
  123. function test_quotes_after_numbers() {
  124. $this->assertSame( 'Class of &#8217;99', wptexturize( "Class of '99" ) );
  125. }
  126. /**
  127. * @ticket 4539
  128. * @ticket 15241
  129. */
  130. function test_other_html() {
  131. $this->assertSame( '&#8216;<strong>', wptexturize( "'<strong>" ) );
  132. // $this->assertSame( '&#8216;<strong>Quoted Text</strong>&#8217;,', wptexturize( "'<strong>Quoted Text</strong>'," ) );
  133. // $this->assertSame( '&#8220;<strong>Quoted Text</strong>&#8221;,', wptexturize( '"<strong>Quoted Text</strong>",' ) );
  134. }
  135. function test_x() {
  136. $this->assertSame( '14&#215;24', wptexturize( '14x24' ) );
  137. }
  138. function test_minutes_seconds() {
  139. $this->assertSame( '9&#8242;', wptexturize( '9\'' ) );
  140. $this->assertSame( '9&#8243;', wptexturize( '9"' ) );
  141. $this->assertSame( 'a 9&#8242; b', wptexturize( 'a 9\' b' ) );
  142. $this->assertSame( 'a 9&#8243; b', wptexturize( 'a 9" b' ) );
  143. $this->assertSame( '&#8220;a 9&#8242; b&#8221;', wptexturize( '"a 9\' b"' ) );
  144. $this->assertSame( '&#8216;a 9&#8243; b&#8217;', wptexturize( "'a 9\" b'" ) );
  145. }
  146. /**
  147. * @ticket 8775
  148. */
  149. function test_wptexturize_quotes_around_numbers() {
  150. $this->assertSame( '&#8220;12345&#8221;', wptexturize( '"12345"' ) );
  151. $this->assertSame( '&#8216;12345&#8217;', wptexturize( '\'12345\'' ) );
  152. $this->assertSame( '&#8220;a 9&#8242; plus a &#8216;9&#8217;, maybe a 9&#8242; &#8216;9&#8217;&#8221;', wptexturize( '"a 9\' plus a \'9\', maybe a 9\' \'9\'"' ) );
  153. $this->assertSame( '<p>&#8217;99<br />&#8216;123&#8217;<br />&#8217;tis<br />&#8216;s&#8217;</p>', wptexturize( '<p>\'99<br />\'123\'<br />\'tis<br />\'s\'</p>' ) );
  154. }
  155. /**
  156. * @ticket 8912
  157. */
  158. function test_wptexturize_html_comments() {
  159. $this->assertSame( '<!--[if !IE]>--><!--<![endif]-->', wptexturize( '<!--[if !IE]>--><!--<![endif]-->' ) );
  160. $this->assertSame( '<!--[if !IE]>"a 9\' plus a \'9\', maybe a 9\' \'9\' "<![endif]-->', wptexturize( '<!--[if !IE]>"a 9\' plus a \'9\', maybe a 9\' \'9\' "<![endif]-->' ) );
  161. $this->assertSame( '<ul><li>Hello.</li><!--<li>Goodbye.</li>--></ul>', wptexturize( '<ul><li>Hello.</li><!--<li>Goodbye.</li>--></ul>' ) );
  162. }
  163. /**
  164. * @ticket 4539
  165. * @ticket 15241
  166. */
  167. function test_entity_quote_cuddling() {
  168. $this->assertSame( '&nbsp;&#8220;Testing&#8221;', wptexturize( '&nbsp;"Testing"' ) );
  169. // $this->assertSame( '&#38;&#8220;Testing&#8221;', wptexturize( '&#38;"Testing"' ) );
  170. }
  171. /**
  172. * @ticket 22823
  173. */
  174. function test_apostrophes_before_primes() {
  175. $this->assertSame( 'WordPress 3.5&#8217;s release date', wptexturize( "WordPress 3.5's release date" ) );
  176. }
  177. /**
  178. * @ticket 23185
  179. */
  180. function test_spaces_around_hyphens() {
  181. $nbsp = "\xC2\xA0";
  182. $this->assertSame( ' &#8211; ', wptexturize( ' - ' ) );
  183. $this->assertSame( '&nbsp;&#8211;&nbsp;', wptexturize( '&nbsp;-&nbsp;' ) );
  184. $this->assertSame( ' &#8211;&nbsp;', wptexturize( ' -&nbsp;' ) );
  185. $this->assertSame( '&nbsp;&#8211; ', wptexturize( '&nbsp;- ' ) );
  186. $this->assertSame( "$nbsp&#8211;$nbsp", wptexturize( "$nbsp-$nbsp" ) );
  187. $this->assertSame( " &#8211;$nbsp", wptexturize( " -$nbsp" ) );
  188. $this->assertSame( "$nbsp&#8211; ", wptexturize( "$nbsp- " ) );
  189. $this->assertSame( ' &#8212; ', wptexturize( ' -- ' ) );
  190. $this->assertSame( '&nbsp;&#8212;&nbsp;', wptexturize( '&nbsp;--&nbsp;' ) );
  191. $this->assertSame( ' &#8212;&nbsp;', wptexturize( ' --&nbsp;' ) );
  192. $this->assertSame( '&nbsp;&#8212; ', wptexturize( '&nbsp;-- ' ) );
  193. $this->assertSame( "$nbsp&#8212;$nbsp", wptexturize( "$nbsp--$nbsp" ) );
  194. $this->assertSame( " &#8212;$nbsp", wptexturize( " --$nbsp" ) );
  195. $this->assertSame( "$nbsp&#8212; ", wptexturize( "$nbsp-- " ) );
  196. }
  197. /**
  198. * @ticket 31030
  199. */
  200. function test_hyphens_at_start_and_end() {
  201. $this->assertSame( '&#8211; ', wptexturize( '- ' ) );
  202. $this->assertSame( '&#8211; &#8211;', wptexturize( '- -' ) );
  203. $this->assertSame( ' &#8211;', wptexturize( ' -' ) );
  204. $this->assertSame( '&#8212; ', wptexturize( '-- ' ) );
  205. $this->assertSame( '&#8212; &#8212;', wptexturize( '-- --' ) );
  206. $this->assertSame( ' &#8212;', wptexturize( ' --' ) );
  207. }
  208. /**
  209. * Test spaces around quotes.
  210. *
  211. * These should never happen, even if the desired output changes some day.
  212. *
  213. * @ticket 22692
  214. */
  215. function test_spaces_around_quotes_never() {
  216. $nbsp = "\xC2\xA0";
  217. $problem_input = "$nbsp\"A";
  218. $problem_output = "$nbsp&#8221;A";
  219. $this->assertNotEquals( $problem_output, wptexturize( $problem_input ) );
  220. }
  221. /**
  222. * Test spaces around quotes.
  223. *
  224. * These are desirable outputs for the current design.
  225. *
  226. * @ticket 22692
  227. * @dataProvider data_spaces_around_quotes
  228. */
  229. function test_spaces_around_quotes( $input, $output ) {
  230. return $this->assertSame( $output, wptexturize( $input ) );
  231. }
  232. function data_spaces_around_quotes() {
  233. $nbsp = "\xC2\xA0";
  234. $pi = "\xCE\xA0";
  235. return array(
  236. array(
  237. "stop. $nbsp\"A quote after 2 spaces.\"",
  238. "stop. $nbsp&#8220;A quote after 2 spaces.&#8221;",
  239. ),
  240. array(
  241. "stop.$nbsp$nbsp\"A quote after 2 spaces.\"",
  242. "stop.$nbsp$nbsp&#8220;A quote after 2 spaces.&#8221;",
  243. ),
  244. array(
  245. "stop. $nbsp'A quote after 2 spaces.'",
  246. "stop. $nbsp&#8216;A quote after 2 spaces.&#8217;",
  247. ),
  248. array(
  249. "stop.$nbsp$nbsp'A quote after 2 spaces.'",
  250. "stop.$nbsp$nbsp&#8216;A quote after 2 spaces.&#8217;",
  251. ),
  252. array(
  253. 'stop. &nbsp;"A quote after 2 spaces."',
  254. 'stop. &nbsp;&#8220;A quote after 2 spaces.&#8221;',
  255. ),
  256. array(
  257. 'stop.&nbsp;&nbsp;"A quote after 2 spaces."',
  258. 'stop.&nbsp;&nbsp;&#8220;A quote after 2 spaces.&#8221;',
  259. ),
  260. array(
  261. "stop. &nbsp;'A quote after 2 spaces.'",
  262. 'stop. &nbsp;&#8216;A quote after 2 spaces.&#8217;',
  263. ),
  264. array(
  265. "stop.&nbsp;&nbsp;'A quote after 2 spaces.'",
  266. 'stop.&nbsp;&nbsp;&#8216;A quote after 2 spaces.&#8217;',
  267. ),
  268. array(
  269. "Contraction: $pi's",
  270. "Contraction: $pi&#8217;s",
  271. ),
  272. );
  273. }
  274. /**
  275. * Apostrophe before a number always becomes &#8217 (apos);
  276. *
  277. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  278. *
  279. * @ticket 22692
  280. * @dataProvider data_apos_before_digits
  281. */
  282. function test_apos_before_digits( $input, $output ) {
  283. return $this->assertSame( $output, wptexturize( $input ) );
  284. }
  285. function data_apos_before_digits() {
  286. return array(
  287. array(
  288. "word '99 word",
  289. 'word &#8217;99 word',
  290. ),
  291. array(
  292. "word'99 word",
  293. 'word&#8217;99 word',
  294. ),
  295. array(
  296. "word '99word",
  297. 'word &#8217;99word',
  298. ),
  299. array(
  300. "word'99word",
  301. 'word&#8217;99word',
  302. ),
  303. array(
  304. "word '99&#8217;s word", // Appears as a separate but logically superfluous pattern in 3.8.
  305. 'word &#8217;99&#8217;s word',
  306. ),
  307. array(
  308. "according to our source, '33 students scored less than 50' on the test.", // Apostrophes and primes have priority over quotes.
  309. 'according to our source, &#8217;33 students scored less than 50&#8242; on the test.',
  310. ),
  311. );
  312. }
  313. /**
  314. * Apostrophe after a space or ([{<" becomes &#8216; (opening_single_quote)
  315. *
  316. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  317. *
  318. * @ticket 22692
  319. * @dataProvider data_opening_single_quote
  320. */
  321. function test_opening_single_quote( $input, $output ) {
  322. return $this->assertSame( $output, wptexturize( $input ) );
  323. }
  324. function data_opening_single_quote() {
  325. return array(
  326. array(
  327. "word 'word word",
  328. 'word &#8216;word word',
  329. ),
  330. array(
  331. "word ('word word",
  332. 'word (&#8216;word word',
  333. ),
  334. array(
  335. "word ['word word",
  336. 'word [&#8216;word word',
  337. ),
  338. array(
  339. "word <'word word", // Invalid HTML.
  340. "word <'word word",
  341. ),
  342. array(
  343. "word &lt;'word word", // Valid HTML input makes curly quotes.
  344. 'word &lt;&#8216;word word',
  345. ),
  346. array(
  347. "word {'word word",
  348. 'word {&#8216;word word',
  349. ),
  350. array(
  351. "word \"'word word",
  352. 'word &#8220;&#8216;word word', // Two opening quotes.
  353. ),
  354. array(
  355. "'word word",
  356. '&#8216;word word',
  357. ),
  358. array(
  359. "word('word word",
  360. 'word(&#8216;word word',
  361. ),
  362. array(
  363. "word['word word",
  364. 'word[&#8216;word word',
  365. ),
  366. array(
  367. "word<'word word",
  368. "word<'word word",
  369. ),
  370. array(
  371. "word&lt;'word word",
  372. 'word&lt;&#8216;word word',
  373. ),
  374. array(
  375. "word{'word word",
  376. 'word{&#8216;word word',
  377. ),
  378. array(
  379. "word\"'word word",
  380. 'word&#8221;&#8216;word word', // Closing quote, then opening quote.
  381. ),
  382. array(
  383. "word ' word word",
  384. 'word &#8216; word word',
  385. ),
  386. array(
  387. "word (' word word",
  388. 'word (&#8216; word word',
  389. ),
  390. array(
  391. "word [' word word",
  392. 'word [&#8216; word word',
  393. ),
  394. array(
  395. "word <' word word",
  396. "word <' word word",
  397. ),
  398. array(
  399. "word &lt;' word word",
  400. 'word &lt;&#8216; word word',
  401. ),
  402. array(
  403. "word {' word word",
  404. 'word {&#8216; word word',
  405. ),
  406. array(
  407. "word \"' word word",
  408. 'word &#8220;&#8216; word word', // Two opening quotes.
  409. ),
  410. array(
  411. "' word word",
  412. '&#8216; word word',
  413. ),
  414. array(
  415. "word(' word word",
  416. 'word(&#8216; word word',
  417. ),
  418. array(
  419. "word[' word word",
  420. 'word[&#8216; word word',
  421. ),
  422. array(
  423. "word<' word word",
  424. "word<' word word",
  425. ),
  426. array(
  427. "word&lt;' word word",
  428. 'word&lt;&#8216; word word',
  429. ),
  430. array(
  431. "word{' word word",
  432. 'word{&#8216; word word',
  433. ),
  434. array(
  435. "word\"' word word",
  436. 'word&#8221;&#8216; word word', // Closing quote, then opening quote.
  437. ),
  438. );
  439. }
  440. /**
  441. * Double quote after a number becomes &#8243; (double_prime)
  442. *
  443. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  444. *
  445. * @ticket 22692
  446. * @dataProvider data_double_prime
  447. */
  448. function test_double_prime( $input, $output ) {
  449. return $this->assertSame( $output, wptexturize( $input ) );
  450. }
  451. function data_double_prime() {
  452. return array(
  453. array(
  454. 'word 99" word',
  455. 'word 99&#8243; word',
  456. ),
  457. array(
  458. 'word 99"word',
  459. 'word 99&#8243;word',
  460. ),
  461. array(
  462. 'word99" word',
  463. 'word99&#8243; word',
  464. ),
  465. array(
  466. 'word99"word',
  467. 'word99&#8243;word',
  468. ),
  469. );
  470. }
  471. /**
  472. * Apostrophe after a number becomes &#8242; (prime)
  473. *
  474. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  475. *
  476. * @ticket 22692
  477. * @dataProvider data_single_prime
  478. */
  479. function test_single_prime( $input, $output ) {
  480. return $this->assertSame( $output, wptexturize( $input ) );
  481. }
  482. function data_single_prime() {
  483. return array(
  484. array(
  485. "word 99' word",
  486. 'word 99&#8242; word',
  487. ),
  488. array(
  489. "word 99'word", // Not a prime anymore. Apostrophes get priority.
  490. 'word 99&#8217;word',
  491. ),
  492. array(
  493. "word99' word",
  494. 'word99&#8242; word',
  495. ),
  496. array(
  497. "word99'word", // Not a prime anymore.
  498. 'word99&#8217;word',
  499. ),
  500. );
  501. }
  502. /**
  503. * Apostrophe "in a word" becomes &#8217; (apos)
  504. *
  505. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  506. *
  507. * @ticket 22692
  508. * @dataProvider data_contractions
  509. */
  510. function test_contractions( $input, $output ) {
  511. return $this->assertSame( $output, wptexturize( $input ) );
  512. }
  513. function data_contractions() {
  514. return array(
  515. array(
  516. "word word's word",
  517. 'word word&#8217;s word',
  518. ),
  519. array(
  520. "word'[ word", // Apostrophes are never followed by opening punctuation.
  521. "word'[ word",
  522. ),
  523. array(
  524. "word'( word",
  525. "word'( word",
  526. ),
  527. array(
  528. "word'{ word",
  529. "word'{ word",
  530. ),
  531. array(
  532. "word'&lt; word",
  533. "word'&lt; word",
  534. ),
  535. array(
  536. "word'< word", // Invalid HTML input does trigger the apos pattern.
  537. 'word&#8217;< word',
  538. ),
  539. );
  540. }
  541. /**
  542. * Double quote after a space or ([-{< becomes &#8220; (opening_quote) if not followed by spaces
  543. *
  544. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  545. *
  546. * @ticket 22692
  547. * @dataProvider data_opening_quote
  548. */
  549. function test_opening_quote( $input, $output ) {
  550. return $this->assertSame( $output, wptexturize( $input ) );
  551. }
  552. function data_opening_quote() {
  553. return array(
  554. array(
  555. 'word "word word',
  556. 'word &#8220;word word',
  557. ),
  558. array(
  559. 'word ("word word',
  560. 'word (&#8220;word word',
  561. ),
  562. array(
  563. 'word ["word word',
  564. 'word [&#8220;word word',
  565. ),
  566. array(
  567. 'word <"word word', // Invalid HTML.
  568. 'word <"word word',
  569. ),
  570. array(
  571. 'word &lt;"word word',
  572. 'word &lt;&#8220;word word',
  573. ),
  574. array(
  575. 'word {"word word',
  576. 'word {&#8220;word word',
  577. ),
  578. array(
  579. 'word -"word word',
  580. 'word -&#8220;word word',
  581. ),
  582. array(
  583. 'word-"word word',
  584. 'word-&#8220;word word',
  585. ),
  586. array(
  587. '"word word',
  588. '&#8220;word word',
  589. ),
  590. array(
  591. 'word("word word',
  592. 'word(&#8220;word word',
  593. ),
  594. array(
  595. 'word["word word',
  596. 'word[&#8220;word word',
  597. ),
  598. array(
  599. 'word<"word word',
  600. 'word<"word word',
  601. ),
  602. array(
  603. 'word&lt;"word word',
  604. 'word&lt;&#8220;word word',
  605. ),
  606. array(
  607. 'word{"word word',
  608. 'word{&#8220;word word',
  609. ),
  610. array(
  611. 'word "99 word',
  612. 'word &#8220;99 word',
  613. ),
  614. );
  615. }
  616. /**
  617. * Double quote becomes &#8221; (closing_quote) unless it is already converted to double_prime or opening_quote.
  618. *
  619. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  620. *
  621. * @ticket 22692
  622. * @dataProvider data_closing_quote
  623. */
  624. function test_closing_quote( $input, $output ) {
  625. return $this->assertSame( $output, wptexturize( $input ) );
  626. }
  627. function data_closing_quote() {
  628. return array(
  629. array(
  630. 'word word" word',
  631. 'word word&#8221; word',
  632. ),
  633. array(
  634. 'word word") word',
  635. 'word word&#8221;) word',
  636. ),
  637. array(
  638. 'word word"] word',
  639. 'word word&#8221;] word',
  640. ),
  641. array(
  642. 'word word"} word',
  643. 'word word&#8221;} word',
  644. ),
  645. array(
  646. 'word word"> word', // Invalid HTML input?
  647. 'word word&#8221;> word',
  648. ),
  649. array(
  650. 'word word"&gt; word', // Valid HTML should work.
  651. 'word word&#8221;&gt; word',
  652. ),
  653. array(
  654. 'word word"',
  655. 'word word&#8221;',
  656. ),
  657. array(
  658. 'word word"word',
  659. 'word word&#8221;word',
  660. ),
  661. array(
  662. 'word"word"word',
  663. 'word&#8221;word&#8221;word',
  664. ),
  665. array(
  666. 'test sentence".',
  667. 'test sentence&#8221;.',
  668. ),
  669. array(
  670. 'test sentence",',
  671. 'test sentence&#8221;,',
  672. ),
  673. array(
  674. 'test sentence":',
  675. 'test sentence&#8221;:',
  676. ),
  677. array(
  678. 'test sentence";',
  679. 'test sentence&#8221;;',
  680. ),
  681. array(
  682. 'test sentence"!',
  683. 'test sentence&#8221;!',
  684. ),
  685. array(
  686. 'test sentence"?',
  687. 'test sentence&#8221;?',
  688. ),
  689. array(
  690. 'test sentence."',
  691. 'test sentence.&#8221;',
  692. ),
  693. array(
  694. 'test sentence". word',
  695. 'test sentence&#8221;. word',
  696. ),
  697. array(
  698. 'test sentence." word',
  699. 'test sentence.&#8221; word',
  700. ),
  701. );
  702. }
  703. /**
  704. * Test that single quotes followed by a space or .,-)}]> become &#8217; (closing_single_quote)
  705. *
  706. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  707. *
  708. * @ticket 22692
  709. * @dataProvider data_closing_single_quote
  710. */
  711. function test_closing_single_quote( $input, $output ) {
  712. return $this->assertSame( $output, wptexturize( $input ) );
  713. }
  714. function data_closing_single_quote() {
  715. return array(
  716. array(
  717. "word word' word",
  718. 'word word&#8217; word',
  719. ),
  720. array(
  721. "word word'. word",
  722. 'word word&#8217;. word',
  723. ),
  724. array(
  725. "word word'.word",
  726. 'word word&#8217;.word',
  727. ),
  728. array(
  729. "word word', she said",
  730. 'word word&#8217;, she said',
  731. ),
  732. array(
  733. "word word': word",
  734. 'word word&#8217;: word',
  735. ),
  736. array(
  737. "word word'; word",
  738. 'word word&#8217;; word',
  739. ),
  740. array(
  741. "word word'! word",
  742. 'word word&#8217;! word',
  743. ),
  744. array(
  745. "word word'? word",
  746. 'word word&#8217;? word',
  747. ),
  748. array(
  749. "word word'- word",
  750. 'word word&#8217;- word',
  751. ),
  752. array(
  753. "word word') word",
  754. 'word word&#8217;) word',
  755. ),
  756. array(
  757. "word word'} word",
  758. 'word word&#8217;} word',
  759. ),
  760. array(
  761. "word word'] word",
  762. 'word word&#8217;] word',
  763. ),
  764. array(
  765. "word word'&gt; word",
  766. 'word word&#8217;&gt; word',
  767. ),
  768. array(
  769. "word word'",
  770. 'word word&#8217;',
  771. ),
  772. array(
  773. "test sentence'.",
  774. 'test sentence&#8217;.',
  775. ),
  776. array(
  777. "test sentence.'",
  778. 'test sentence.&#8217;',
  779. ),
  780. array(
  781. "test sentence'. word",
  782. 'test sentence&#8217;. word',
  783. ),
  784. array(
  785. "test sentence.' word",
  786. 'test sentence.&#8217; word',
  787. ),
  788. );
  789. }
  790. /**
  791. * Tests multiplication.
  792. *
  793. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  794. *
  795. * @ticket 22692
  796. * @ticket 30445
  797. * @dataProvider data_multiplication
  798. */
  799. function test_multiplication( $input, $output ) {
  800. return $this->assertSame( $output, wptexturize( $input ) );
  801. }
  802. function data_multiplication() {
  803. return array(
  804. array(
  805. '9x9',
  806. '9&#215;9',
  807. ),
  808. array(
  809. '12x34',
  810. '12&#215;34',
  811. ),
  812. array(
  813. '-123x1=-123',
  814. '-123&#215;1=-123',
  815. ),
  816. // @ticket 30445
  817. array(
  818. '-123x-1',
  819. '-123x-1',
  820. ),
  821. array(
  822. '0.675x1=0.675',
  823. '0.675&#215;1=0.675',
  824. ),
  825. array(
  826. '9 x 9',
  827. '9 x 9',
  828. ),
  829. array(
  830. '0x70',
  831. '0x70',
  832. ),
  833. array(
  834. '3x2x1x0',
  835. '3x2x1x0',
  836. ),
  837. );
  838. }
  839. /**
  840. * Test ampersands. & always becomes &#038; unless it is followed by # or ;
  841. *
  842. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  843. *
  844. * @ticket 22692
  845. * @dataProvider data_ampersand
  846. */
  847. function test_ampersand( $input, $output ) {
  848. return $this->assertSame( $output, wptexturize( $input ) );
  849. }
  850. function data_ampersand() {
  851. return array(
  852. array(
  853. 'word & word',
  854. 'word &#038; word',
  855. ),
  856. array(
  857. 'word&word',
  858. 'word&#038;word',
  859. ),
  860. array(
  861. 'word &nbsp; word',
  862. 'word &nbsp; word',
  863. ),
  864. array(
  865. 'word &#038; word',
  866. 'word &#038; word',
  867. ),
  868. array(
  869. 'word &#xabc; word',
  870. 'word &#xabc; word',
  871. ),
  872. array(
  873. 'word &#X394; word',
  874. 'word &#X394; word',
  875. ),
  876. array(
  877. 'word &# word',
  878. 'word &#038;# word',
  879. ),
  880. array(
  881. 'word &44; word',
  882. 'word &44; word',
  883. ),
  884. array(
  885. 'word &&amp; word',
  886. 'word &#038;&amp; word',
  887. ),
  888. array(
  889. 'word &!amp; word',
  890. 'word &#038;!amp; word',
  891. ),
  892. array(
  893. 'word &#',
  894. 'word &#038;#',
  895. ),
  896. array(
  897. 'word &',
  898. 'word &#038;',
  899. ),
  900. );
  901. }
  902. /**
  903. * Test "cockney" phrases, which begin with an apostrophe instead of an opening single quote.
  904. *
  905. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  906. *
  907. * @ticket 22692
  908. * @dataProvider data_cockney
  909. */
  910. function test_cockney( $input, $output ) {
  911. return $this->assertSame( $output, wptexturize( $input ) );
  912. }
  913. function data_cockney() {
  914. return array(
  915. array(
  916. "word 'tain't word",
  917. 'word &#8217;tain&#8217;t word',
  918. ),
  919. array(
  920. "word 'twere word",
  921. 'word &#8217;twere word',
  922. ),
  923. array(
  924. "word 'twas word",
  925. 'word &#8217;twas word',
  926. ),
  927. array(
  928. "word 'tis word",
  929. 'word &#8217;tis word',
  930. ),
  931. array(
  932. "word 'twill word",
  933. 'word &#8217;twill word',
  934. ),
  935. array(
  936. "word 'til word",
  937. 'word &#8217;til word',
  938. ),
  939. array(
  940. "word 'bout word",
  941. 'word &#8217;bout word',
  942. ),
  943. array(
  944. "word 'nuff word",
  945. 'word &#8217;nuff word',
  946. ),
  947. array(
  948. "word 'round word",
  949. 'word &#8217;round word',
  950. ),
  951. array(
  952. "word 'cause word",
  953. 'word &#8217;cause word',
  954. ),
  955. array(
  956. "word 'em word",
  957. 'word &#8217;em word',
  958. ),
  959. );
  960. }
  961. /**
  962. * Test smart dashes.
  963. *
  964. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  965. *
  966. * @ticket 22692
  967. * @dataProvider data_smart_dashes
  968. */
  969. function test_smart_dashes( $input, $output ) {
  970. return $this->assertSame( $output, wptexturize( $input ) );
  971. }
  972. function data_smart_dashes() {
  973. return array(
  974. array(
  975. 'word --- word',
  976. 'word &#8212; word',
  977. ),
  978. array(
  979. 'word---word',
  980. 'word&#8212;word',
  981. ),
  982. array(
  983. 'word -- word',
  984. 'word &#8212; word',
  985. ),
  986. array(
  987. 'word--word',
  988. 'word&#8211;word',
  989. ),
  990. array(
  991. 'word - word',
  992. 'word &#8211; word',
  993. ),
  994. array(
  995. 'word-word',
  996. 'word-word',
  997. ),
  998. array(
  999. 'word xn&#8211; word',
  1000. 'word xn&#8211; word',
  1001. ),
  1002. array(
  1003. 'wordxn&#8211;word',
  1004. 'wordxn&#8211;word',
  1005. ),
  1006. array(
  1007. 'wordxn--word',
  1008. 'wordxn--word',
  1009. ),
  1010. );
  1011. }
  1012. /**
  1013. * Test miscellaneous static replacements.
  1014. *
  1015. * Checks all baseline patterns. If anything ever changes in wptexturize(), these tests may fail.
  1016. *
  1017. * @ticket 22692
  1018. * @dataProvider data_misc_static_replacements
  1019. */
  1020. function test_misc_static_replacements( $input, $output ) {
  1021. return $this->assertSame( $output, wptexturize( $input ) );
  1022. }
  1023. function data_misc_static_replacements() {
  1024. return array(
  1025. array(
  1026. 'word ... word',
  1027. 'word &#8230; word',
  1028. ),
  1029. array(
  1030. 'word...word',
  1031. 'word&#8230;word',
  1032. ),
  1033. array(
  1034. 'word `` word',
  1035. 'word &#8220; word',
  1036. ),
  1037. array(
  1038. 'word``word',
  1039. 'word&#8220;word',
  1040. ),
  1041. array(
  1042. "word '' word",
  1043. 'word &#8221; word',
  1044. ),
  1045. array(
  1046. "word''word",
  1047. 'word&#8221;word',
  1048. ),
  1049. array(
  1050. 'word (tm) word',
  1051. 'word &#8482; word',
  1052. ),
  1053. array(
  1054. 'word (tm)word',
  1055. 'word &#8482;word',
  1056. ),
  1057. array(
  1058. 'word(tm) word',
  1059. 'word(tm) word',
  1060. ),
  1061. array(
  1062. 'word(tm)word',
  1063. 'word(tm)word',
  1064. ),
  1065. );
  1066. }
  1067. /**
  1068. * Numbers inside of matching quotes get curly quotes instead of apostrophes and primes.
  1069. *
  1070. * @ticket 8775
  1071. * @dataProvider data_quoted_numbers
  1072. */
  1073. function test_quoted_numbers( $input, $output ) {
  1074. return $this->assertSame( $output, wptexturize( $input ) );
  1075. }
  1076. function data_quoted_numbers() {
  1077. return array(
  1078. array(
  1079. 'word "42.00" word',
  1080. 'word &#8220;42.00&#8221; word',
  1081. ),
  1082. array(
  1083. 'word "42.00"word',
  1084. 'word &#8220;42.00&#8221;word',
  1085. ),
  1086. array(
  1087. "word '42.00' word",
  1088. 'word &#8216;42.00&#8217; word',
  1089. ),
  1090. array(
  1091. "word '42.00'word",
  1092. 'word &#8216;42.00&#8217;word',
  1093. ),
  1094. array(
  1095. 'word "42" word',
  1096. 'word &#8220;42&#8221; word',
  1097. ),
  1098. array(
  1099. 'word "42,00" word',
  1100. 'word &#8220;42,00&#8221; word',
  1101. ),
  1102. array(
  1103. 'word "4,242.00" word',
  1104. 'word &#8220;4,242.00&#8221; word',
  1105. ),
  1106. array(
  1107. "word '99's word",
  1108. 'word &#8217;99&#8217;s word',
  1109. ),
  1110. array(
  1111. "word '99'samsonite",
  1112. 'word &#8217;99&#8217;samsonite',
  1113. ),
  1114. );
  1115. }
  1116. /**
  1117. * Quotations should be allowed to have dashes around them.
  1118. *
  1119. * @ticket 20342
  1120. * @dataProvider data_quotes_and_dashes
  1121. */
  1122. function test_quotes_and_dashes( $input, $output ) {
  1123. return $this->assertSame( $output, wptexturize( $input ) );
  1124. }
  1125. function data_quotes_and_dashes() {
  1126. return array(
  1127. array(
  1128. 'word---"quote"',
  1129. 'word&#8212;&#8220;quote&#8221;',
  1130. ),
  1131. array(
  1132. 'word--"quote"',
  1133. 'word&#8211;&#8220;quote&#8221;',
  1134. ),
  1135. array(
  1136. 'word-"quote"',
  1137. 'word-&#8220;quote&#8221;',
  1138. ),
  1139. array(
  1140. "word---'quote'",
  1141. 'word&#8212;&#8216;quote&#8217;',
  1142. ),
  1143. array(
  1144. "word--'quote'",
  1145. 'word&#8211;&#8216;quote&#8217;',
  1146. ),
  1147. array(
  1148. "word-'quote'",
  1149. 'word-&#8216;quote&#8217;',
  1150. ),
  1151. array(
  1152. '"quote"---word',
  1153. '&#8220;quote&#8221;&#8212;word',
  1154. ),
  1155. array(
  1156. '"quote"--word',
  1157. '&#8220;quote&#8221;&#8211;word',
  1158. ),
  1159. array(
  1160. '"quote"-word',
  1161. '&#8220;quote&#8221;-word',
  1162. ),
  1163. array(
  1164. "'quote'---word",
  1165. '&#8216;quote&#8217;&#8212;word',
  1166. ),
  1167. array(
  1168. "'quote'--word",
  1169. '&#8216;quote&#8217;&#8211;word',
  1170. ),
  1171. array(
  1172. "'quote'-word",
  1173. '&#8216;quote&#8217;-word',
  1174. ),
  1175. );
  1176. }
  1177. /**
  1178. * Test HTML and shortcode avoidance.
  1179. *
  1180. * @ticket 12690
  1181. * @dataProvider data_tag_avoidance
  1182. */
  1183. function test_tag_avoidance( $input, $output ) {
  1184. return $this->assertSame( $output, wptexturize( $input ) );
  1185. }
  1186. function data_tag_avoidance() {
  1187. return array(
  1188. array(
  1189. '[ ... ]',
  1190. '[ &#8230; ]',
  1191. ),
  1192. array(
  1193. '[ is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1194. '[ is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1195. ),
  1196. array(
  1197. '[is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1198. '[is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1199. ),
  1200. array(
  1201. '[caption - is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1202. '[caption &#8211; is it wise to <a title="allow user content ] here? hmm"> maybe </a> ]',
  1203. ),
  1204. array(
  1205. '[ photos by <a href="http://example.com/?a[]=1&a[]=2"> this guy & that guy </a> ]',
  1206. '[ photos by <a href="http://example.com/?a[]=1&#038;a[]=2"> this guy &#038; that guy </a> ]',
  1207. ),
  1208. array(
  1209. '[photos by <a href="http://example.com/?a[]=1&a[]=2"> this guy & that guy </a>]',
  1210. '[photos by <a href="http://example.com/?a[]=1&#038;a[]=2"> this guy &#038; that guy </a>]',
  1211. ),
  1212. array(
  1213. '& <script>&&</script>',
  1214. '&#038; <script>&&</script>',
  1215. ),
  1216. array(
  1217. '[gallery ...]',
  1218. '[gallery ...]',
  1219. ),
  1220. array(
  1221. '[[gallery ...]', // This tag is still valid.
  1222. '[[gallery ...]',
  1223. ),
  1224. array(
  1225. '[gallery ...]]', // This tag is also valid.
  1226. '[gallery ...]]',
  1227. ),
  1228. array(
  1229. '[/gallery ...]', // This would actually be ignored by the shortcode system.
  1230. '[/gallery ...]', // The decision to not texturize it is intentional, if not correct.
  1231. ),
  1232. array(
  1233. '[[gallery]]...[[/gallery]]', // Shortcode parsing will ignore the inner ']...[' part and treat this as a single escaped shortcode.
  1234. '[[gallery]]&#8230;[[/gallery]]',
  1235. ),
  1236. array(
  1237. '[[[gallery]]]...[[[/gallery]]]', // Again, shortcode parsing matches, but only the '[[gallery]' and '[/gallery]]' parts.
  1238. '[[[gallery]]]&#8230;[[[/gallery]]]',
  1239. ),
  1240. array(
  1241. '[gallery ...',
  1242. '[gallery &#8230;',
  1243. ),
  1244. array(
  1245. '[gallery <br ... /> ...]', // This tag is still valid. Shortcode 'attributes' are not considered
  1246. '[gallery <br ... /> ...]', // in the initial parsing of shortcodes, and HTML is allowed.
  1247. ),
  1248. array(
  1249. '<br [gallery ...] ... />',
  1250. '<br [gallery ...] ... />',
  1251. ),
  1252. array(
  1253. '<br [gallery ...] ... /',
  1254. '<br [gallery ...] ... /',
  1255. ),
  1256. array(
  1257. '<br ... />',
  1258. '<br ... />',
  1259. ),
  1260. array(
  1261. '<br ... />...<br ... />',
  1262. '<br ... />&#8230;<br ... />',
  1263. ),
  1264. array(
  1265. '[gallery ...]...[gallery ...]',
  1266. '[gallery ...]&#8230;[gallery ...]',
  1267. ),
  1268. array(
  1269. '[[gallery ...]]',
  1270. '[[gallery ...]]',
  1271. ),
  1272. array(
  1273. '[[gallery ...]',
  1274. '[[gallery ...]',
  1275. ),
  1276. array(
  1277. '[gallery ...]]',
  1278. '[gallery ...]]',
  1279. ),
  1280. array(
  1281. '[/gallery ...]]',
  1282. '[/gallery ...]]',
  1283. ),
  1284. array(
  1285. '[[gallery <br ... /> ...]]', // This gets parsed as an escaped shortcode with embedded HTML. Brains may explode.
  1286. '[[gallery <br ... /> ...]]',
  1287. ),
  1288. array(
  1289. '<br [[gallery ...]] ... />',
  1290. '<br [[gallery ...]] ... />',
  1291. ),
  1292. array(
  1293. '<br [[gallery ...]] ... /',
  1294. '<br [[gallery ...]] ... /',
  1295. ),
  1296. array(
  1297. '[[gallery ...]]...[[gallery ...]]',
  1298. '[[gallery ...]]&#8230;[[gallery ...]]',
  1299. ),
  1300. array(
  1301. '[[gallery ...]...[/gallery]]',
  1302. '[[gallery ...]&#8230;[/gallery]]',
  1303. ),
  1304. array(
  1305. '<!-- ... -->',
  1306. '<!-- ... -->',
  1307. ),
  1308. array(
  1309. '<!--...-->',
  1310. '<!--...-->',
  1311. ),
  1312. array(
  1313. '<!-- ... -- > ...',
  1314. '<!-- ... -- > ...',
  1315. ),
  1316. array(
  1317. '<!-- ...', // An unclosed comment is still a comment.
  1318. '<!-- ...',
  1319. ),
  1320. array(
  1321. 'a<!-->b', // Browsers seem to allow this.
  1322. 'a<!-->b',
  1323. ),
  1324. array(
  1325. 'a<!--->b',
  1326. 'a<!--->b',
  1327. ),
  1328. array(
  1329. 'a<!---->b',
  1330. 'a<!---->b',
  1331. ),
  1332. array(
  1333. 'a<!----->b',
  1334. 'a<!----->b',
  1335. ),
  1336. array(
  1337. 'a<!-- c --->b',
  1338. 'a<!-- c --->b',
  1339. ),
  1340. array(
  1341. 'a<!-- c -- d -->b',
  1342. 'a<!-- c -- d -->b',
  1343. ),
  1344. array(
  1345. 'a<!-- <!-- c --> -->b<!-- close -->',
  1346. 'a<!-- <!-- c --> &#8211;>b<!-- close -->',
  1347. ),
  1348. array(
  1349. '<!-- <br /> [gallery] ... -->',
  1350. '<!-- <br /> [gallery] ... -->',
  1351. ),
  1352. array(
  1353. '...<!-- ... -->...',
  1354. '&#8230;<!-- ... -->&#8230;',
  1355. ),
  1356. array(
  1357. '[gallery ...]...<!-- ... -->...<br ... />',
  1358. '[gallery ...]&#8230;<!-- ... -->&#8230;<br ... />',
  1359. ),
  1360. array(
  1361. '<ul><li>Hello.</li><!--<li>Goodbye.</li>--></ul>',
  1362. '<ul><li>Hello.</li><!--<li>Goodbye.</li>--></ul>',
  1363. ),
  1364. array(
  1365. 'word <img src="http://example.com/wp-content/uploads/2014/06/image-300x216.gif" /> word', // Ensure we are not corrupting image URLs.
  1366. 'word <img src="http://example.com/wp-content/uploads/2014/06/image-300x216.gif" /> word',
  1367. ),
  1368. array(
  1369. '[ do texturize "[quote]" here ]',
  1370. '[ do texturize &#8220;[quote]&#8221; here ]',
  1371. ),
  1372. array(
  1373. '[ regex catches this <a href="[quote]">here</a> ]',
  1374. '[ regex catches this <a href="[quote]">here</a> ]',
  1375. ),
  1376. array(
  1377. '[ but also catches the <b>styled "[quote]" here</b> ]',
  1378. '[ but also catches the <b>styled &#8220;[quote]&#8221; here</b> ]',
  1379. ),
  1380. array(
  1381. '[Let\'s get crazy<input>[caption code="<a href=\'?a[]=100\'>hello</a>"]</input>world]', // [caption] shortcode is invalid here because it contains '[]' chars.
  1382. '[Let&#8217;s get crazy<input>[caption code=&#8221;<a href=\'?a[]=100\'>hello</a>&#8220;]</input>world]',
  1383. ),
  1384. array(
  1385. '<> ... <>',
  1386. '<> &#8230; <>',
  1387. ),
  1388. array(
  1389. '<> ... <> ... >',
  1390. '<> &#8230; <> &#8230; >',
  1391. ),
  1392. array(
  1393. '<> ... < ... > ... <>',
  1394. '<> &#8230; < ... > &#8230; <>',
  1395. ),
  1396. );
  1397. }
  1398. /**
  1399. * Year abbreviations consist of exactly two digits.
  1400. *
  1401. * @ticket 26850
  1402. * @dataProvider data_year_abbr
  1403. */
  1404. function test_year_abbr( $input, $output ) {
  1405. return $this->assertSame( $output, wptexturize( $input ) );
  1406. }
  1407. function data_year_abbr() {
  1408. return array(
  1409. array(
  1410. "word '99 word",
  1411. 'word &#8217;99 word',
  1412. ),
  1413. array(
  1414. "word '99. word",
  1415. 'word &#8217;99. word',
  1416. ),
  1417. array(
  1418. "word '99, word",
  1419. 'word &#8217;99, word',
  1420. ),
  1421. array(
  1422. "word '99; word",
  1423. 'word &#8217;99; word',
  1424. ),
  1425. array(
  1426. "word '99' word", // For this pattern, prime doesn't make sense. Should get apos and a closing quote.
  1427. 'word &#8217;99&#8217; word',
  1428. ),
  1429. array(
  1430. "word '99'. word",
  1431. 'word &#8217;99&#8217;. word',
  1432. ),
  1433. array(
  1434. "word '99', word",
  1435. 'word &#8217;99&#8217;, word',
  1436. ),
  1437. array(
  1438. "word '99.' word",
  1439. 'word &#8217;99.&#8217; word',
  1440. ),
  1441. array(
  1442. "word '99",
  1443. 'word &#8217;99',
  1444. ),
  1445. array(
  1446. "'99 word",
  1447. '&#8217;99 word',
  1448. ),
  1449. array(
  1450. "word '999 word", // Does not match the apos pattern, should be opening quote.
  1451. 'word &#8216;999 word',
  1452. ),
  1453. array(
  1454. "word '99% word",
  1455. 'word &#8216;99% word',
  1456. ),
  1457. array(
  1458. "word '9 word",
  1459. 'word &#8216;9 word',
  1460. ),
  1461. array(
  1462. "word '99.9 word",
  1463. 'word &#8216;99.9 word',
  1464. ),
  1465. array(
  1466. "word '999",
  1467. 'word &#8216;999',
  1468. ),
  1469. array(
  1470. "word '9",
  1471. 'word &#8216;9',
  1472. ),
  1473. array(
  1474. "in '4 years, 3 months,' Obama cut the deficit",
  1475. 'in &#8216;4 years, 3 months,&#8217; Obama cut the deficit',
  1476. ),
  1477. array(
  1478. "testing's '4' through 'quotes'",
  1479. 'testing&#8217;s &#8216;4&#8217; through &#8216;quotes&#8217;',
  1480. ),
  1481. );
  1482. }
  1483. /**
  1484. * Make sure translation actually works.
  1485. *
  1486. * Also make sure apostrophes and closing quotes aren't being confused by default.
  1487. *
  1488. * @ticket 27426
  1489. * @dataProvider data_translate
  1490. */
  1491. function test_translate( $input, $output ) {
  1492. add_filter( 'gettext_with_context', array( $this, 'filter_translate' ), 10, 4 );
  1493. $result = wptexturize( $input, true );
  1494. remove_filter( 'gettext_with_context', array( $this, 'filter_translate' ), 10, 4 );
  1495. wptexturize( 'reset', true );
  1496. return $this->assertSame( $output, $result );
  1497. }
  1498. function filter_translate( $translations, $text, $context, $domain ) {
  1499. switch ( $text ) {
  1500. case '&#8211;':
  1501. return '!endash!';
  1502. case '&#8212;':
  1503. return '!emdash!';
  1504. case '&#8216;':
  1505. return '!openq1!';
  1506. case '&#8217;':
  1507. if ( 'apostrophe' === $context ) {
  1508. return '!apos!';
  1509. } else {
  1510. return '!closeq1!';
  1511. }
  1512. case '&#8220;':
  1513. return '!openq2!';
  1514. case '&#8221;':
  1515. return '!closeq2!';
  1516. case '&#8242;':
  1517. return '!prime1!';
  1518. case '&#8243;':
  1519. return '!prime2!';
  1520. case '&#8217;tain&#8217;t,&#8217;twere,&#8217;twas,&#8217;tis,&#8217;twill,&#8217;til,&#8217;bout,&#8217;nuff,&#8217;round,&#8217;cause,&#8217;em':
  1521. return '!apos!tain!apos!t,!apos!twere,!apos!twas,!apos!tis,!apos!twill,!apos!til,!apos!bout,!apos!nuff,!apos!round,!apos!cause,!apos!em';
  1522. default:
  1523. return $translations;
  1524. }
  1525. }
  1526. function data_translate() {
  1527. return array(
  1528. array(
  1529. "word '99 word",
  1530. 'word !apos!99 word',
  1531. ),
  1532. array(
  1533. "word'99 word",
  1534. 'word!apos!99 word',
  1535. ),
  1536. array(
  1537. "word 'test sentence' word",
  1538. 'word !openq1!test sentence!closeq1! word',
  1539. ),
  1540. array(
  1541. "'test sentence'",
  1542. '!openq1!test sentence!closeq1!',
  1543. ),
  1544. array(
  1545. 'word "test sentence" word',
  1546. 'word !openq2!test sentence!closeq2! word',
  1547. ),
  1548. array(
  1549. '"test sentence"',
  1550. '!openq2!test sentence!closeq2!',
  1551. ),
  1552. array(
  1553. "word 'word word",
  1554. 'word !openq1!word word',
  1555. ),
  1556. array(
  1557. "word ('word word",
  1558. 'word (!openq1!word word',
  1559. ),
  1560. array(
  1561. "word ['word word",
  1562. 'word [!openq1!word word',
  1563. ),
  1564. array(
  1565. 'word 99" word',
  1566. 'word 99!prime2! word',
  1567. ),
  1568. array(
  1569. 'word 99"word',
  1570. 'word 99!prime2!word',
  1571. ),
  1572. array(
  1573. 'word99" word',
  1574. 'word99!prime2! word',
  1575. ),
  1576. array(
  1577. 'word99"word',
  1578. 'word99!prime2!word',
  1579. ),
  1580. array(
  1581. "word 99' word",
  1582. 'word 99!prime1! word',
  1583. ),
  1584. array(
  1585. "word99' word",
  1586. 'word99!prime1! word',
  1587. ),
  1588. array(
  1589. "word word's word",
  1590. 'word word!apos!s word',
  1591. ),
  1592. array(
  1593. "word word'. word",
  1594. 'word word!closeq1!. word',
  1595. ),
  1596. array(
  1597. "word ]'. word",
  1598. 'word ]!closeq1!. word',
  1599. ),
  1600. array(
  1601. 'word "word word',
  1602. 'word !openq2!word word',
  1603. ),
  1604. array(
  1605. 'word ("word word',
  1606. 'word (!openq2!word word',
  1607. ),
  1608. array(
  1609. 'word ["word word',
  1610. 'word [!openq2!word word',
  1611. ),
  1612. array(
  1613. 'word word" word',
  1614. 'word word!closeq2! word',
  1615. ),
  1616. array(
  1617. 'word word") word',
  1618. 'word word!closeq2!) word',
  1619. ),
  1620. array(
  1621. 'word word"] word',
  1622. 'word word!closeq2!] word',
  1623. ),
  1624. array(
  1625. 'word word"',
  1626. 'word word!closeq2!',
  1627. ),
  1628. array(
  1629. 'word word"word',
  1630. 'word word!closeq2!word',
  1631. ),
  1632. array(
  1633. 'test sentence".',
  1634. 'test sentence!closeq2!.',
  1635. ),
  1636. array(
  1637. 'test sentence."',
  1638. 'test sentence.!closeq2!',
  1639. ),
  1640. array(
  1641. 'test sentence." word',
  1642. 'test sentence.!closeq2! word',
  1643. ),
  1644. array(
  1645. "word word' word",
  1646. 'word word!closeq1! word',
  1647. ),
  1648. array(
  1649. "word word'. word",
  1650. 'word word!closeq1!. word',
  1651. ),
  1652. array(
  1653. "word word'.word",
  1654. 'word word!closeq1!.word',
  1655. ),
  1656. array(
  1657. "word word'",
  1658. 'word word!closeq1!',
  1659. ),
  1660. array(
  1661. "test sentence'.",
  1662. 'test sentence!closeq1!.',
  1663. ),
  1664. array(
  1665. "test sentence.'",
  1666. 'test sentence.!closeq1!',
  1667. ),
  1668. array(
  1669. "test sentence'. word",
  1670. 'test sentence!closeq1!. word',
  1671. ),
  1672. array(
  1673. "test sentence.' word",
  1674. 'test sentence.!closeq1! word',
  1675. ),
  1676. array(
  1677. "word 'tain't word",
  1678. 'word !apos!tain!apos!t word',
  1679. ),
  1680. array(
  1681. "word 'twere word",
  1682. 'word !apos!twere word',
  1683. ),
  1684. array(
  1685. 'word "42.00" word',
  1686. 'word !openq2!42.00!closeq2! word',
  1687. ),
  1688. array(
  1689. "word '42.00' word",
  1690. 'word !openq1!42.00!closeq1! word',
  1691. ),
  1692. array(
  1693. "word word'. word",
  1694. 'word word!closeq1!. word',
  1695. ),
  1696. array(
  1697. "word word'.word",
  1698. 'word word!closeq1!.word',
  1699. ),
  1700. array(
  1701. "word word', she said",
  1702. 'word word!closeq1!, she said',
  1703. ),
  1704. );
  1705. }
  1706. /**
  1707. * Extra sanity checks for _wptexturize_pushpop_element()
  1708. *
  1709. * @ticket 28483
  1710. * @dataProvider data_element_stack
  1711. */
  1712. function test_element_stack( $input, $output ) {
  1713. return $this->assertSame( $output, wptexturize( $input ) );
  1714. }
  1715. function data_element_stack() {
  1716. return array(
  1717. array(
  1718. '<span>hello</code>---</span>',
  1719. '<span>hello</code>&#8212;</span>',
  1720. ),
  1721. array(
  1722. '</code>hello<span>---</span>',
  1723. '</code>hello<span>&#8212;</span>',
  1724. ),
  1725. array(
  1726. '<code>hello</code>---</span>',
  1727. '<code>hello</code>&#8212;</span>',
  1728. ),
  1729. array(
  1730. '<span>hello</span>---<code>',
  1731. '<span>hello</span>&#8212;<code>',
  1732. ),
  1733. array(
  1734. '<span>hello<code>---</span>',
  1735. '<span>hello<code>---</span>',
  1736. ),
  1737. array(
  1738. '<code>hello<span>---</span>',
  1739. '<code>hello<span>---</span>',
  1740. ),
  1741. array(
  1742. '<code>hello</span>---</span>',
  1743. '<code>hello</span>---</span>',
  1744. ),
  1745. array(
  1746. '<span><code>hello</code>---</span>',
  1747. '<span><code>hello</code>&#8212;</span>',
  1748. ),
  1749. array(
  1750. '<code>hello</code>world<span>---</span>',
  1751. '<code>hello</code>world<span>&#8212;</span>',
  1752. ),
  1753. );
  1754. }
  1755. /**
  1756. * Test disabling shortcode texturization.
  1757. *
  1758. * @ticket 29557
  1759. * @dataProvider data_unregistered_shortcodes
  1760. */
  1761. function test_unregistered_shortcodes( $input, $output ) {
  1762. add_filter( 'no_texturize_shortcodes', array( $this, 'filter_shortcodes' ), 10, 1 );
  1763. $output = $this->assertSame( $output, wptexturize( $input ) );
  1764. remove_filter( 'no_texturize_shortcodes', array( $this, 'filter_shortcodes' ), 10, 1 );
  1765. return $output;
  1766. }
  1767. function filter_shortcodes( $disabled ) {
  1768. $disabled[] = 'audio';
  1769. return $disabled;
  1770. }
  1771. function data_unregistered_shortcodes() {
  1772. return array(
  1773. array(
  1774. '[a]a--b[audio]---[/audio]a--b[/a]',
  1775. '[a]a&#8211;b[audio]---[/audio]a&#8211;b[/a]',
  1776. ),
  1777. array(
  1778. '[code ...]...[/code]', // '[code]' is not a registered shortcode.
  1779. '[code &#8230;]&#8230;[/code]',
  1780. ),
  1781. array(
  1782. '[hello ...]...[/hello]', // '[hello]' is not a registered shortcode.
  1783. '[hello &#8230;]&#8230;[/hello]',
  1784. ),
  1785. array(
  1786. '[...]...[/...]', // These are potentially usable shortcodes.
  1787. '[&#8230;]&#8230;[/&#8230;]',
  1788. ),
  1789. array(
  1790. '[gal>ery ...]',
  1791. '[gal>ery &#8230;]',
  1792. ),
  1793. array(
  1794. '[randomthing param="test"]',
  1795. '[randomthing param=&#8221;test&#8221;]',
  1796. ),
  1797. array(
  1798. '[[audio]...[/audio]...', // These are potentially usable shortcodes.
  1799. '[[audio]&#8230;[/audio]&#8230;', // Unfortunately, the meaning of [[audio] is ambiguous unless we run the entire shortcode regexp.
  1800. ),
  1801. array(
  1802. '[audio]...[/audio]]...', // These are potentially usable shortcodes.
  1803. '[audio]...[/audio]]...', // Unfortunately, the meaning of [/audio]] is ambiguous unless we run the entire shortcode regexp.
  1804. ), // This test would not pass in 3.9 because the extra brace was always ignored by texturize.
  1805. array(
  1806. '<span>hello[/audio]---</span>',
  1807. '<span>hello[/audio]&#8212;</span>',
  1808. ),
  1809. array(
  1810. '[/audio]hello<span>---</span>',
  1811. '[/audio]hello<span>&#8212;</span>',
  1812. ),
  1813. array(
  1814. '[audio]hello[/audio]---</span>',
  1815. '[audio]hello[/audio]&#8212;</span>',
  1816. ),
  1817. array(
  1818. '<span>hello</span>---[audio]',
  1819. '<span>hello</span>&#8212;[audio]',
  1820. ),
  1821. array(
  1822. '<span>hello[audio]---</span>',
  1823. '<span>hello[audio]---</span>',
  1824. ),
  1825. array(
  1826. '[audio]hello<span>---</span>',
  1827. '[audio]hello<span>---</span>',
  1828. ),
  1829. array(
  1830. '[audio]hello</span>---</span>',
  1831. '[audio]hello</span>---</span>',
  1832. ),
  1833. );
  1834. }
  1835. /**
  1836. * Ensure primes logic is not too greedy at the end of a quotation.
  1837. *
  1838. * @ticket 29256
  1839. * @dataProvider data_primes_vs_quotes
  1840. */
  1841. function test_primes_vs_quotes( $input, $output ) {
  1842. return $this->assertSame( $output, wptexturize( $input ) );
  1843. }
  1844. function data_primes_vs_quotes() {
  1845. return array(
  1846. array(
  1847. "George's porch is 99' long.",
  1848. 'George&#8217;s porch is 99&#8242; long.',
  1849. ),
  1850. array(
  1851. 'The best year "was that time in 2012" when everyone partied, he said.',
  1852. 'The best year &#8220;was that time in 2012&#8221; when everyone partied, he said.',
  1853. ),
  1854. array(
  1855. "I need 4 x 20' = 80' of trim.", // Works only with a space before the '=' char.
  1856. 'I need 4 x 20&#8242; = 80&#8242; of trim.',
  1857. ),
  1858. array(
  1859. '"Lorem ipsum dolor sit amet 1234"',
  1860. '&#8220;Lorem ipsum dolor sit amet 1234&#8221;',
  1861. ),
  1862. array(
  1863. "'Etiam eu egestas dui 1234'",
  1864. '&#8216;Etiam eu egestas dui 1234&#8217;',
  1865. ),
  1866. array(
  1867. 'according to our source, "33% of all students scored less than 50" on the test.',
  1868. 'according to our source, &#8220;33% of all students scored less than 50&#8221; on the test.',
  1869. ),
  1870. array(
  1871. "The doctor said, 'An average height is between 5' and 6' in study group 7'. He then produced a 6' chart of averages. A man of 7', incredibly, is very possible.",
  1872. 'The doctor said, &#8216;An average height is between 5&#8242; and 6&#8242; in study group 7&#8217;. He then produced a 6&#8242; chart of averages. A man of 7&#8242;, incredibly, is very possible.',
  1873. ),
  1874. array(
  1875. 'Pirates have voted on "The Expendables 3" with their clicks -- and it turns out the Sylvester Stallone-starrer hasn\'t been astoundingly popular among digital thieves, relatively speaking.
  1876. As of Sunday, 5.12 million people worldwide had pirated "Expendables 3" since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio.
  1877. That likely contributed to the action movie\'s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after "Captain America: The Winter Soldier" (7.31 million), "Divergent" (6.29 million) and "The Amazing Spider-Man 2" (5.88 million). Moreover, that\'s despite "Expendables 3" becoming available more than three weeks prior to the film\'s U.S. theatrical debut.
  1878. String with a number followed by a single quote \'Expendables 3\' vestibulum in arcu mi.',
  1879. 'Pirates have voted on &#8220;The Expendables 3&#8221; with their clicks &#8212; and it turns out the Sylvester Stallone-starrer hasn&#8217;t been astoundingly popular among digital thieves, relatively speaking.
  1880. As of Sunday, 5.12 million people worldwide had pirated &#8220;Expendables 3&#8221; since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio.
  1881. That likely contributed to the action movie&#8217;s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after &#8220;Captain America: The Winter Soldier&#8221; (7.31 million), &#8220;Divergent&#8221; (6.29 million) and &#8220;The Amazing Spider-Man 2&#8221; (5.88 million). Moreover, that&#8217;s despite &#8220;Expendables 3&#8221; becoming available more than three weeks prior to the film&#8217;s U.S. theatrical debut.
  1882. String with a number followed by a single quote &#8216;Expendables 3&#8217; vestibulum in arcu mi.',
  1883. ),
  1884. );
  1885. }
  1886. /**
  1887. * Make sure translation actually works.
  1888. *
  1889. * Also make sure opening and closing quotes are allowed to be identical.
  1890. *
  1891. * @ticket 29256
  1892. * @dataProvider data_primes_quotes_translation
  1893. */
  1894. function test_primes_quotes_translation( $input, $output ) {
  1895. add_filter( 'gettext_with_context', array( $this, 'filter_translate2' ), 10, 4 );
  1896. $result = wptexturize( $input, true );
  1897. remove_filter( 'gettext_with_context', array( $this, 'filter_translate2' ), 10, 4 );
  1898. wptexturize( 'reset', true );
  1899. return $this->assertSame( $output, $result );
  1900. }
  1901. function filter_translate2( $translations, $text, $context, $domain ) {
  1902. switch ( $text ) {
  1903. case '&#8211;':
  1904. return '!endash!';
  1905. case '&#8212;':
  1906. return '!emdash!';
  1907. case '&#8216;':
  1908. return '!q1!';
  1909. case '&#8217;':
  1910. if ( 'apostrophe' === $context ) {
  1911. return '!apos!';
  1912. } else {
  1913. return '!q1!';
  1914. }
  1915. case '&#8220;':
  1916. return '!q2!';
  1917. case '&#8221;':
  1918. return '!q2!';
  1919. case '&#8242;':
  1920. return '!prime1!';
  1921. case '&#8243;':
  1922. return '!prime2!';
  1923. default:
  1924. return $translations;
  1925. }
  1926. }
  1927. function data_primes_quotes_translation() {
  1928. return array(
  1929. array(
  1930. "George's porch is 99' long.",
  1931. 'George!apos!s porch is 99!prime1! long.',
  1932. ),
  1933. array(
  1934. 'The best year "was that time in 2012" when everyone partied, he said.',
  1935. 'The best year !q2!was that time in 2012!q2! when everyone partied, he said.',
  1936. ),
  1937. array(
  1938. "I need 4 x 20' = 80' of trim.", // Works only with a space before the '=' char.
  1939. 'I need 4 x 20!prime1! = 80!prime1! of trim.',
  1940. ),
  1941. array(
  1942. '"Lorem ipsum dolor sit amet 1234"',
  1943. '!q2!Lorem ipsum dolor sit amet 1234!q2!',
  1944. ),
  1945. array(
  1946. "'Etiam eu egestas dui 1234'",
  1947. '!q1!Etiam eu egestas dui 1234!q1!',
  1948. ),
  1949. array(
  1950. 'according to our source, "33% of all students scored less than 50" on the test.',
  1951. 'according to our source, !q2!33% of all students scored less than 50!q2! on the test.',
  1952. ),
  1953. array(
  1954. "The doctor said, 'An average height is between 5' and 6' in study group 7'. He then produced a 6' chart of averages. A man of 7', incredibly, is very possible.",
  1955. 'The doctor said, !q1!An average height is between 5!prime1! and 6!prime1! in study group 7!q1!. He then produced a 6!prime1! chart of averages. A man of 7!prime1!, incredibly, is very possible.',
  1956. ),
  1957. array(
  1958. 'Pirates have voted on "The Expendables 3" with their clicks -- and it turns out the Sylvester Stallone-starrer hasn\'t been astoundingly popular among digital thieves, relatively speaking.
  1959. As of Sunday, 5.12 million people worldwide had pirated "Expendables 3" since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio.
  1960. That likely contributed to the action movie\'s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after "Captain America: The Winter Soldier" (7.31 million), "Divergent" (6.29 million) and "The Amazing Spider-Man 2" (5.88 million). Moreover, that\'s despite "Expendables 3" becoming available more than three weeks prior to the film\'s U.S. theatrical debut.
  1961. String with a number followed by a single quote \'Expendables 3\' vestibulum in arcu mi.',
  1962. 'Pirates have voted on !q2!The Expendables 3!q2! with their clicks !emdash! and it turns out the Sylvester Stallone-starrer hasn!apos!t been astoundingly popular among digital thieves, relatively speaking.
  1963. As of Sunday, 5.12 million people worldwide had pirated !q2!Expendables 3!q2! since a high-quality copy hit torrent-sharing sites July 23, according to piracy-tracking firm Excipio.
  1964. That likely contributed to the action movie!apos!s dismal box-office debut this weekend. But over the same July 23-Aug. 18 time period, the movie was No. 4 in downloads, after !q2!Captain America: The Winter Soldier!q2! (7.31 million), !q2!Divergent!q2! (6.29 million) and !q2!The Amazing Spider-Man 2!q2! (5.88 million). Moreover, that!apos!s despite !q2!Expendables 3!q2! becoming available more than three weeks prior to the film!apos!s U.S. theatrical debut.
  1965. String with a number followed by a single quote !q1!Expendables 3!q1! vestibulum in arcu mi.',
  1966. ),
  1967. );
  1968. }
  1969. /**
  1970. * Automated performance testing of the main regex.
  1971. *
  1972. * @dataProvider data_whole_posts
  1973. */
  1974. function test_pcre_performance( $input ) {
  1975. global $shortcode_tags;
  1976. // With shortcodes disabled.
  1977. $regex = _get_wptexturize_split_regex();
  1978. $result = benchmark_pcre_backtracking( $regex, $input, 'split' );
  1979. $this->assertLessThan( 200, $result );
  1980. // With shortcodes enabled.
  1981. $shortcode_regex = _get_wptexturize_shortcode_regex( array_keys( $shortcode_tags ) );
  1982. $regex = _get_wptexturize_split_regex( $shortcode_regex );
  1983. $result = benchmark_pcre_backtracking( $regex, $input, 'split' );
  1984. return $this->assertLessThan( 200, $result );
  1985. }
  1986. /**
  1987. * Ensure that a trailing less-than symbol doesn't cause a PHP warning.
  1988. *
  1989. * @ticket 35864
  1990. */
  1991. function test_trailing_less_than() {
  1992. $this->assertSame( 'F&#8211;oo<', wptexturize( 'F--oo<', true ) );
  1993. }
  1994. function data_whole_posts() {
  1995. require_once DIR_TESTDATA . '/formatting/whole-posts.php';
  1996. return data_whole_posts();
  1997. }
  1998. }