PageRenderTime 31ms CodeModel.GetById 43ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/tests/csslib_test.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 1324 lines | 874 code | 242 blank | 208 comment | 1 complexity | d22120469295bdc64fccf2891e4b9a88 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * This file contains the unittests for the css optimiser in csslib.php
  18. *
  19. * @package core_css
  20. * @category phpunit
  21. * @copyright 2012 Sam Hemelryk
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. global $CFG;
  26. require_once($CFG->libdir . '/csslib.php');
  27. /**
  28. * CSS optimiser test class.
  29. *
  30. * @package core_css
  31. * @category phpunit
  32. * @copyright 2012 Sam Hemelryk
  33. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34. */
  35. class core_csslib_testcase extends advanced_testcase {
  36. /**
  37. * Returns a CSS optimiser
  38. *
  39. * @return css_optimiser
  40. */
  41. protected function get_optimiser() {
  42. return new css_optimiser();
  43. }
  44. /**
  45. * Background colour tests.
  46. *
  47. * When testing background styles it is important to understand how the background shorthand works.
  48. * background shorthand allows the following styles to be specified in a single "background" declaration:
  49. * - background-color
  50. * - background-image
  51. * - background-repeat
  52. * - background-attachment
  53. * - background-position
  54. *
  55. * If the background shorthand is used it can contain one or more of those (preferabbly in that order).
  56. * Important it does not need to contain all of them.
  57. * However even if it doesn't contain values for all styles all of the styles will be affected.
  58. * If a style is missed from the shorthand background style but has an existing value in the rule then the existing value
  59. * is cleared.
  60. *
  61. * For example:
  62. * .test {background: url(\'test.png\');background: bottom right;background:#123456;}
  63. * will result in:
  64. * .test {background:#123456;}
  65. *
  66. * And:
  67. * .test {background-image: url(\'test.png\');background:#123456;}
  68. * will result in:
  69. * .test {background:#123456;}
  70. */
  71. public function test_background() {
  72. $optimiser = new css_optimiser();
  73. $cssin = '.test {background-color: #123456;}';
  74. $cssout = '.test{background-color:#123456;}';
  75. $this->assertSame($cssout, $optimiser->process($cssin));
  76. $cssin = '.test {background: #123456;}';
  77. $cssout = '.test{background:#123456;}';
  78. $this->assertSame($cssout, $optimiser->process($cssin));
  79. $cssin = '.test {background-image: url(\'test.png\');}';
  80. $cssout = '.test{background-image:url(\'test.png\');}';
  81. $this->assertSame($cssout, $optimiser->process($cssin));
  82. $cssin = '.test {background: #123456 url(\'test.png\') no-repeat top left;}';
  83. $cssout = '.test{background:#123456 url(\'test.png\') no-repeat top left;}';
  84. $this->assertSame($cssout, $optimiser->process($cssin));
  85. // Check out this for madness, background position and background-repeat have been reversed.
  86. $cssin = '.test {background: #123456 url(\'test.png\') center no-repeat;}';
  87. $cssout = '.test{background:#123456 url(\'test.png\') no-repeat center;}';
  88. $this->assertSame($cssout, $optimiser->process($cssin));
  89. $cssin = '.test {background: url(\'test.png\') no-repeat top left;}.test{background-position: bottom right}.test {background-color:#123456;}';
  90. $cssout = '.test{background:#123456 url(\'test.png\') no-repeat bottom right;}';
  91. $this->assertSame($cssout, $optimiser->process($cssin));
  92. $cssin = '.test {background: url( \'test.png\' )}.test{background: bottom right}.test {background:#123456;}';
  93. $cssout = '.test{background:#123456;}';
  94. $this->assertSame($cssout, $optimiser->process($cssin));
  95. $cssin = '.test {background-image: url(\'test.png\');background:#123456;}';
  96. $cssout = '.test{background:#123456;}';
  97. $this->assertSame($cssout, $optimiser->process($cssin));
  98. $cssin = '.test {background-color: #123456;background-repeat: repeat-x; background-position: 100% 0%;}';
  99. $cssout = '.test{background-color:#123456;background-repeat:repeat-x;background-position:100% 0%;}';
  100. $this->assertSame($cssout, $optimiser->process($cssin));
  101. $cssin = '.tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 10%;background-repeat: no-repeat;}
  102. .tree_item.branch.navigation_node {background-image:none;padding-left:0;}';
  103. $cssout = '.tree_item.branch{background-image:url([[pix:t/expanded]]);background-position:0 10%;background-repeat:no-repeat;} .tree_item.branch.navigation_node{background-image:none;padding-left:0;}';
  104. $this->assertSame($cssout, $optimiser->process($cssin));
  105. $cssin = '#nextLink{background:url(data:image/gif;base64,AAAA);}';
  106. $cssout = '#nextLink{background:url(data:image/gif;base64,AAAA);}';
  107. $this->assertSame($cssout, $optimiser->process($cssin));
  108. $cssin = '#nextLink{background-image:url(data:image/gif;base64,AAAA);}';
  109. $cssout = '#nextLink{background-image:url(data:image/gif;base64,AAAA);}';
  110. $this->assertSame($cssout, $optimiser->process($cssin));
  111. $cssin = '.test {background: #123456 url(data:image/gif;base64,AAAA) no-repeat top left;}';
  112. $cssout = '.test{background:#123456 url(data:image/gif;base64,AAAA) no-repeat top left;}';
  113. $this->assertSame($cssout, $optimiser->process($cssin));
  114. $cssin = '#test {background-image:none;background-position:right center;background-repeat:no-repeat;}';
  115. $cssout = '#test{background-image:none;background-position:right center;background-repeat:no-repeat;}';
  116. $this->assertSame($cssout, $optimiser->process($cssin));
  117. $cssin = '.test {background: url([[pix:theme|photos]]) no-repeat 50% 50%;background-size: 40px 40px;-webkit-background-size: 40px 40px;}';
  118. $cssout = '.test{background:url([[pix:theme|photos]]) no-repeat 50% 50%;background-size:40px 40px;-webkit-background-size:40px 40px;}';
  119. $this->assertSame($cssout, $optimiser->process($cssin));
  120. $cssin = '.test{background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);}';
  121. $cssout = '.test{background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
  122. $this->assertSame($cssout, $optimiser->process($cssin));
  123. $cssin = '.test{background-image: -moz-linear-gradient(#3c3c3c, #111);background-image: -webkit-linear-gradient(#3c3c3c, #111);background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);background-image: url(/test.png);}';
  124. $cssout = '.test{background-image:url(/test.png);background-image:-moz-linear-gradient(#3c3c3c, #111);background-image:-webkit-linear-gradient(#3c3c3c, #111);background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
  125. $this->assertSame($cssout, $optimiser->process($cssin));
  126. $cssin = '.test{background:#CCC; background-image: url(test.png);}';
  127. $cssout = '.test{background:#CCC url(test.png);}';
  128. $this->assertSame($cssout, $optimiser->process($cssin));
  129. $cssin = '.test{background:#CCC; background-image: linear-gradient(#3c3c3c, #111);}';
  130. $cssout = '.test{background:#CCC;background-image:linear-gradient(#3c3c3c, #111);}';
  131. $this->assertSame($cssout, $optimiser->process($cssin));
  132. $cssin = '.test{background:#CCC; background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);}';
  133. $cssout = '.test{background:#CCC;background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
  134. $this->assertSame($cssout, $optimiser->process($cssin));
  135. $cssin = '#newmessageoverlay{font-weight: normal; border: 1px solid #222; background: #444; color: #ddd; text-shadow: 0 -1px 0px #000; background-image: -moz-linear-gradient(top, #333 0%, #333 5%, #444 15%, #444 60%, #222 100%); background-image: -webkit-gradient(linear, center top, center bottom, color-stop(0, #333), color-stop(5%, #333), color-stop(15%, #444), color-stop(60%, #444), color-stop(1, #222)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=\'#333333\', EndColorStr=\'#222222\')"; padding:20px; padding-left: 0px; padding-right: 10px; position: inherit; z-index: 9999; width: 90%; margin-left: auto; margin-right: auto; height: 100%;}';
  136. $cssout = '#newmessageoverlay{font-weight:normal;border:1px solid #222;background:#444;color:#DDD;text-shadow:0 -1px 0px #000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=\'#333333\', EndColorStr=\'#222222\')";padding:20px 10px 20px 0;position:inherit;z-index:9999;width:90%;margin-left:auto;margin-right:auto;height:100%;background-image:-moz-linear-gradient(top, #333 0%, #333 5%, #444 15%, #444 60%, #222 100%);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #333), color-stop(5%, #333), color-stop(15%, #444), color-stop(60%, #444), color-stop(1, #222));}';
  137. $this->assertSame($cssout, $optimiser->process($cssin));
  138. $cssin = '.userenrolment {background-color:inherit !important;background: inherit !important;}';
  139. $cssout = '.userenrolment{background:inherit !important;}';
  140. $this->assertSame($cssout, $optimiser->process($cssin));
  141. $cssin = '.userenrolment {background-image:url(test.png) !important;background: inherit !important;}';
  142. $cssout = '.userenrolment{background:inherit !important;}';
  143. $this->assertSame($cssout, $optimiser->process($cssin));
  144. $cssin = '.userenrolment {background: inherit !important;background-image:url(test.png) !important;}';
  145. $cssout = '.userenrolment{background:inherit url(test.png) !important;}';
  146. $this->assertSame($cssout, $optimiser->process($cssin));
  147. $cssin = '.userenrolment {background: inherit !important;background-image:url(test.png);}';
  148. $cssout = '.userenrolment{background:inherit !important;}';
  149. $this->assertSame($cssout, $optimiser->process($cssin));
  150. $css = '#filesskin .yui3-widget-hd{background:#CCC;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC));background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC);}';
  151. $this->assertSame($css, $optimiser->process($css));
  152. $css = '.userenrolment{background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}';
  153. $this->assertSame($css, $optimiser->process($css));
  154. $css = '.userenrolment{background:#CCC !important;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC)) !important;background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}';
  155. $this->assertSame($css, $optimiser->process($css));
  156. $cssin = '.userenrolment{background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}.userenrolment {background: #CCCCCC!important;background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC))!important;}';
  157. $cssout = '.userenrolment{background:#CCC !important;background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC)) !important;}';
  158. $this->assertSame($cssout, $optimiser->process($cssin));
  159. }
  160. /**
  161. * Border tests.
  162. */
  163. public function test_borders() {
  164. $optimiser = new css_optimiser();
  165. $cssin = '.test {border: 1px solid #654321} .test {border-bottom-color: #123456}';
  166. $cssout = '.test{border:1px solid;border-color:#654321 #654321 #123456;}';
  167. $this->assertSame($cssout, $optimiser->process($cssin));
  168. $cssin = '.one {border:1px solid red;}';
  169. $cssout = '.one{border:1px solid #F00;}';
  170. $this->assertSame($cssout, $optimiser->process($cssin));
  171. $cssin = '.one {border:1px solid;} .one {border:2px dotted #DDD;}';
  172. $cssout = '.one{border:2px dotted #DDD;}';
  173. $this->assertSame($cssout, $optimiser->process($cssin));
  174. $cssin = '.one {border:2px dotted #DDD;}.one {border:1px solid;} ';
  175. $cssout = '.one{border:1px solid #DDD;}';
  176. $this->assertSame($cssout, $optimiser->process($cssin));
  177. $cssin = '.one, .two {border:1px solid red;}';
  178. $cssout = ".one, .two{border:1px solid #F00;}";
  179. $this->assertSame($cssout, $optimiser->process($cssin));
  180. $cssin = '.one, .two {border:0px;}';
  181. $cssout = ".one, .two{border-width:0;}";
  182. $this->assertSame($cssout, $optimiser->process($cssin));
  183. $cssin = '.one, .two {border: thin;}';
  184. $cssout = ".one, .two{border-width:thin;}";
  185. $this->assertSame($cssout, $optimiser->process($cssin));
  186. $cssin = '.one, .two {border: thin solid black;}';
  187. $cssout = ".one, .two{border:thin solid #000;}";
  188. $this->assertSame($cssout, $optimiser->process($cssin));
  189. $cssin = '.one, .two {border-top: 5px solid white;}';
  190. $cssout = ".one, .two{border-top:5px solid #FFF;}";
  191. $this->assertSame($cssout, $optimiser->process($cssin));
  192. $cssin = '.one {border:1px solid red;} .two {border:1px solid red;}';
  193. $cssout = ".one, .two{border:1px solid #F00;}";
  194. $this->assertSame($cssout, $optimiser->process($cssin));
  195. $cssin = '.one {border:1px solid red;width:20px;} .two {border:1px solid red;height:20px;}';
  196. $cssout = ".one{border:1px solid #F00;width:20px;} .two{border:1px solid #F00;height:20px;}";
  197. $this->assertSame($cssout, $optimiser->process($cssin));
  198. $cssin = '.test {border: 1px solid #123456;} .test {border-color: #654321}';
  199. $cssout = '.test{border:1px solid #654321;}';
  200. $this->assertSame($cssout, $optimiser->process($cssin));
  201. $cssin = '.test {border-width: 1px; border-style: solid; border-color: #123456;}';
  202. $cssout = '.test{border:1px solid #123456;}';
  203. $this->assertSame($cssout, $optimiser->process($cssin));
  204. $cssin = '.test {border:1px solid #123456;border-top:2px dotted #654321;}';
  205. $cssout = '.test{border:1px solid #123456;border-top:2px dotted #654321;}';
  206. $this->assertSame($cssout, $optimiser->process($cssin));
  207. $cssin = '.test {border:1px solid #123456;border-left:2px dotted #654321;}';
  208. $cssout = '.test{border:1px solid #123456;border-left:2px dotted #654321;}';
  209. $this->assertSame($cssout, $optimiser->process($cssin));
  210. $cssin = '.test {border-left:2px dotted #654321;border:1px solid #123456;}';
  211. $cssout = '.test{border:1px solid #123456;}';
  212. $this->assertSame($cssout, $optimiser->process($cssin));
  213. $cssin = '.test {border:1px solid;border-top-color:#123456;}';
  214. $cssout = '.test{border:1px solid;border-top-color:#123456;}';
  215. $this->assertSame($cssout, $optimiser->process($cssin));
  216. $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;}';
  217. $cssout = '.test{border:1px solid;border-top-color:#111;border-bottom-color:#222;border-left-color:#333;}';
  218. $this->assertSame($cssout, $optimiser->process($cssin));
  219. $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;border-right-color:#444;}';
  220. $cssout = '.test{border:1px solid;border-color:#111 #444 #222 #333;}';
  221. $this->assertSame($cssout, $optimiser->process($cssin));
  222. $cssin = '.generaltable .cell {border-color:#EEE;} .generaltable .cell {border-width: 1px;border-style: solid;}';
  223. $cssout = '.generaltable .cell{border:1px solid #EEE;}';
  224. $this->assertSame($cssout, $optimiser->process($cssin));
  225. $cssin = '#page-admin-roles-override .rolecap {border:none;border-bottom:1px solid #CECECE;}';
  226. $cssout = '#page-admin-roles-override .rolecap{border-top:0;border-right:0;border-bottom:1px solid #CECECE;border-left:0;}';
  227. $this->assertSame($cssout, $optimiser->process($cssin));
  228. }
  229. /**
  230. * Test colour styles.
  231. */
  232. public function test_colors() {
  233. $optimiser = new css_optimiser();
  234. $css = '.css{}';
  235. $this->assertSame($css, $optimiser->process($css));
  236. $css = '.css{color:#123456;}';
  237. $this->assertSame($css, $optimiser->process($css));
  238. $css = '#some{color:#123456;}';
  239. $this->assertSame($css, $optimiser->process($css));
  240. $css = 'div{color:#123456;}';
  241. $this->assertSame($css, $optimiser->process($css));
  242. $css = 'div.css{color:#123456;}';
  243. $this->assertSame($css, $optimiser->process($css));
  244. $css = 'div#some{color:#123456;}';
  245. $this->assertSame($css, $optimiser->process($css));
  246. $css = 'div[type=blah]{color:#123456;}';
  247. $this->assertSame($css, $optimiser->process($css));
  248. $css = 'div.css[type=blah]{color:#123456;}';
  249. $this->assertSame($css, $optimiser->process($css));
  250. $css = 'div#some[type=blah]{color:#123456;}';
  251. $this->assertSame($css, $optimiser->process($css));
  252. $css = '#some.css[type=blah]{color:#123456;}';
  253. $this->assertSame($css, $optimiser->process($css));
  254. $css = '#some .css[type=blah]{color:#123456;}';
  255. $this->assertSame($css, $optimiser->process($css));
  256. $cssin = '.one {color:red;} .two {color:#F00;}';
  257. $cssout = ".one, .two{color:#F00;}";
  258. $this->assertSame($cssout, $optimiser->process($cssin));
  259. $cssin = '.one {color:#123;color:#321;}';
  260. $cssout = '.one{color:#321;}';
  261. $this->assertSame($cssout, $optimiser->process($cssin));
  262. $cssin = '.one {color:#123; color : #321 ;}';
  263. $cssout = '.one{color:#321;}';
  264. $this->assertSame($cssout, $optimiser->process($cssin));
  265. $cssin = '.one {color:#123;} .one {color:#321;}';
  266. $cssout = '.one{color:#321;}';
  267. $this->assertSame($cssout, $optimiser->process($cssin));
  268. $cssin = '.one {color:#123 !important;color:#321;}';
  269. $cssout = '.one{color:#123 !important;}';
  270. $this->assertSame($cssout, $optimiser->process($cssin));
  271. $cssin = '.one {color:#123 !important;} .one {color:#321;}';
  272. $cssout = '.one{color:#123 !important;}';
  273. $this->assertSame($cssout, $optimiser->process($cssin));
  274. $cssin = '.one {color:#123!important;} .one {color:#321;}';
  275. $cssout = '.one{color:#123 !important;}';
  276. $this->assertSame($cssout, $optimiser->process($cssin));
  277. $cssin = '.one {color:rgb(255, 128, 1)}';
  278. $cssout = '.one{color:rgb(255, 128, 1);}';
  279. $this->assertSame($cssout, $optimiser->process($cssin));
  280. $cssin = '.one {color:rgba(255, 128, 1, 0.5)}';
  281. $cssout = '.one{color:rgba(255, 128, 1, 0.5);}';
  282. $this->assertSame($cssout, $optimiser->process($cssin));
  283. $cssin = '.one {color:hsl(120, 65%, 75%)}';
  284. $cssout = '.one{color:hsl(120, 65%, 75%);}';
  285. $this->assertSame($cssout, $optimiser->process($cssin));
  286. $cssin = '.one {color:hsla(120,65%,75%,0.5)}';
  287. $cssout = '.one{color:hsla(120,65%,75%,0.5);}';
  288. $this->assertSame($cssout, $optimiser->process($cssin));
  289. // Try some invalid colours to make sure we don't mangle them.
  290. $css = 'div#some{color:#1;}';
  291. $this->assertSame($css, $optimiser->process($css));
  292. $css = 'div#some{color:#12;}';
  293. $this->assertSame($css, $optimiser->process($css));
  294. $css = 'div#some{color:#1234;}';
  295. $this->assertSame($css, $optimiser->process($css));
  296. $css = 'div#some{color:#12345;}';
  297. $this->assertSame($css, $optimiser->process($css));
  298. }
  299. /**
  300. * Test widths.
  301. */
  302. public function test_widths() {
  303. $optimiser = new css_optimiser();
  304. $cssin = '.css {width:0}';
  305. $cssout = '.css{width:0;}';
  306. $this->assertSame($cssout, $optimiser->process($cssin));
  307. $cssin = '.css {width:0px}';
  308. $cssout = '.css{width:0;}';
  309. $this->assertSame($cssout, $optimiser->process($cssin));
  310. $cssin = '.css {width:0em}';
  311. $cssout = '.css{width:0;}';
  312. $this->assertSame($cssout, $optimiser->process($cssin));
  313. $cssin = '.css {width:0pt}';
  314. $cssout = '.css{width:0;}';
  315. $this->assertSame($cssout, $optimiser->process($cssin));
  316. $cssin = '.css {width:0mm}';
  317. $cssout = '.css{width:0;}';
  318. $this->assertSame($cssout, $optimiser->process($cssin));
  319. $cssin = '.css {width:100px}';
  320. $cssout = '.css{width:100px;}';
  321. $this->assertSame($cssout, $optimiser->process($cssin));
  322. }
  323. /**
  324. * Test margin styles.
  325. */
  326. public function test_margins() {
  327. $optimiser = new css_optimiser();
  328. $cssin = '.one {margin: 1px 2px 3px 4px}';
  329. $cssout = '.one{margin:1px 2px 3px 4px;}';
  330. $this->assertSame($cssout, $optimiser->process($cssin));
  331. $cssin = '.one {margin-top:1px; margin-left:4px; margin-right:2px; margin-bottom: 3px;}';
  332. $cssout = '.one{margin:1px 2px 3px 4px;}';
  333. $this->assertSame($cssout, $optimiser->process($cssin));
  334. $cssin = '.one {margin-top:1px; margin-left:4px;}';
  335. $cssout = '.one{margin-top:1px;margin-left:4px;}';
  336. $this->assertSame($cssout, $optimiser->process($cssin));
  337. $cssin = '.one {margin:1px; margin-left:4px;}';
  338. $cssout = '.one{margin:1px 1px 1px 4px;}';
  339. $this->assertSame($cssout, $optimiser->process($cssin));
  340. $cssin = '.one {margin:1px; margin-bottom:4px;}';
  341. $cssout = '.one{margin:1px 1px 4px;}';
  342. $this->assertSame($cssout, $optimiser->process($cssin));
  343. $cssin = '.one, .two, .one.two, .one .two {margin:0;} .one.two {margin:0 7px;}';
  344. $cssout = '.one, .two{margin:0;} .one.two{margin:0 7px;} .one .two{margin:0;}';
  345. $this->assertSame($cssout, $optimiser->process($cssin));
  346. $cssin = '.block {margin-top: 0px !important;margin-bottom: 0px !important;}';
  347. $cssout = '.block{margin-top:0 !important;margin-bottom:0 !important;}';
  348. $this->assertSame($cssout, $optimiser->process($cssin));
  349. $cssin = '.block {margin: 0px !important;margin-bottom: 3px;}';
  350. $cssout = '.block{margin:0 !important;}';
  351. $this->assertSame($cssout, $optimiser->process($cssin));
  352. $cssin = '.block {margin: 5px;margin-right: 0 !important;}';
  353. $cssout = '.block{margin:5px;margin-right:0 !important;}';
  354. $this->assertSame($cssout, $optimiser->process($cssin));
  355. }
  356. /**
  357. * Test padding styles.
  358. */
  359. public function test_padding() {
  360. $optimiser = new css_optimiser();
  361. $cssin = '.one {padding: 1px 2px 3px 4px}';
  362. $cssout = '.one{padding:1px 2px 3px 4px;}';
  363. $this->assertSame($cssout, $optimiser->process($cssin));
  364. $cssin = '.one {padding-top:1px; padding-left:4px; padding-right:2px; padding-bottom: 3px;}';
  365. $cssout = '.one{padding:1px 2px 3px 4px;}';
  366. $this->assertSame($cssout, $optimiser->process($cssin));
  367. $cssin = '.one {padding-top:1px; padding-left:4px;padding-bottom: 3px;}';
  368. $cssout = '.one{padding-top:1px;padding-left:4px;padding-bottom:3px;}';
  369. $this->assertSame($cssout, $optimiser->process($cssin));
  370. $cssin = '.one {padding-top:1px; padding-left:4px;}';
  371. $cssout = '.one{padding-top:1px;padding-left:4px;}';
  372. $this->assertSame($cssout, $optimiser->process($cssin));
  373. $cssin = '.one {padding:1px; padding-left:4px;}';
  374. $cssout = '.one{padding:1px 1px 1px 4px;}';
  375. $this->assertSame($cssout, $optimiser->process($cssin));
  376. $cssin = '.one {padding:1px; padding-bottom:4px;}';
  377. $cssout = '.one{padding:1px 1px 4px;}';
  378. $this->assertSame($cssout, $optimiser->process($cssin));
  379. $cssin = '.one {padding:0 !important;}';
  380. $cssout = '.one{padding:0 !important;}';
  381. $this->assertSame($cssout, $optimiser->process($cssin));
  382. $cssin = '.one {padding:0 !important;}';
  383. $cssout = '.one{padding:0 !important;}';
  384. $this->assertSame($cssout, $optimiser->process($cssin));
  385. $cssin = '.one, .two, .one.two, .one .two {padding:0;} .one.two {padding:0 7px;}';
  386. $cssout = '.one, .two{padding:0;} .one.two{padding:0 7px;} .one .two{padding:0;}';
  387. $this->assertSame($cssout, $optimiser->process($cssin));
  388. $cssin = '.block {padding-top: 0px !important;padding-bottom: 0px !important;}';
  389. $cssout = '.block{padding-top:0 !important;padding-bottom:0 !important;}';
  390. $this->assertSame($cssout, $optimiser->process($cssin));
  391. $cssin = '.block {padding: 0px !important;padding-bottom: 3px;}';
  392. $cssout = '.block{padding:0 !important;}';
  393. $this->assertSame($cssout, $optimiser->process($cssin));
  394. $cssin = '.block {padding: 5px;padding-right: 0 !important;}';
  395. $cssout = '.block{padding:5px;padding-right:0 !important;}';
  396. $this->assertSame($cssout, $optimiser->process($cssin));
  397. }
  398. /**
  399. * Test cursor optimisations
  400. */
  401. public function test_cursor() {
  402. $optimiser = new css_optimiser();
  403. // Valid cursor.
  404. $cssin = '.one {cursor: pointer;}';
  405. $cssout = '.one{cursor:pointer;}';
  406. $this->assertSame($cssout, $optimiser->process($cssin));
  407. // Invalid cursor but tolerated.
  408. $cssin = '.one {cursor: hand;}';
  409. $cssout = '.one{cursor:hand;}';
  410. $this->assertSame($cssout, $optimiser->process($cssin));
  411. // Valid cursor: url relative.
  412. $cssin = '.one {cursor: mycursor.png;}';
  413. $cssout = '.one{cursor:mycursor.png;}';
  414. $this->assertSame($cssout, $optimiser->process($cssin));
  415. // Valid cursor: url absolute.
  416. $cssin = '.one {cursor: http://local.host/mycursor.png;}';
  417. $cssout = '.one{cursor:http://local.host/mycursor.png;}';
  418. $this->assertSame($cssout, $optimiser->process($cssin));
  419. }
  420. /**
  421. * Test vertical align optimisations
  422. */
  423. public function test_vertical_align() {
  424. $optimiser = new css_optimiser();
  425. // Valid vertical aligns.
  426. $cssin = '.one {vertical-align: baseline;}';
  427. $cssout = '.one{vertical-align:baseline;}';
  428. $this->assertSame($cssout, $optimiser->process($cssin));
  429. $cssin = '.one {vertical-align: middle;}';
  430. $cssout = '.one{vertical-align:middle;}';
  431. $this->assertSame($cssout, $optimiser->process($cssin));
  432. $cssin = '.one {vertical-align: 0.75em;}';
  433. $cssout = '.one{vertical-align:0.75em;}';
  434. $this->assertSame($cssout, $optimiser->process($cssin));
  435. $cssin = '.one {vertical-align: 50%;}';
  436. $cssout = '.one{vertical-align:50%;}';
  437. $this->assertSame($cssout, $optimiser->process($cssin));
  438. // Invalid but tolerated.
  439. $cssin = '.one {vertical-align: center;}';
  440. $cssout = '.one{vertical-align:center;}';
  441. $this->assertSame($cssout, $optimiser->process($cssin));
  442. }
  443. /**
  444. * Test float optimisations
  445. */
  446. public function test_float() {
  447. $optimiser = new css_optimiser();
  448. // Valid vertical aligns.
  449. $cssin = '.one {float: inherit;}';
  450. $cssout = '.one{float:inherit;}';
  451. $this->assertSame($cssout, $optimiser->process($cssin));
  452. $cssin = '.one {float: left;}';
  453. $cssout = '.one{float:left;}';
  454. $this->assertSame($cssout, $optimiser->process($cssin));
  455. $cssin = '.one {float: right;}';
  456. $cssout = '.one{float:right;}';
  457. $this->assertSame($cssout, $optimiser->process($cssin));
  458. $cssin = '.one {float: none;}';
  459. $cssout = '.one{float:none;}';
  460. $this->assertSame($cssout, $optimiser->process($cssin));
  461. // Invalid but tolerated.
  462. $cssin = '.one {float: center;}';
  463. $cssout = '.one{float:center;}';
  464. $this->assertSame($cssout, $optimiser->process($cssin));
  465. }
  466. /**
  467. * Test some totally invalid CSS optimisation.
  468. */
  469. public function test_invalid_css_handling() {
  470. $optimiser = new css_optimiser();
  471. $cssin = array(
  472. '.one{}',
  473. '.one {:}',
  474. '.one {;}',
  475. '.one {;;;;;}',
  476. '.one {:;}',
  477. '.one {:;:;:;:::;;;}',
  478. '.one {!important}',
  479. '.one {:!important}',
  480. '.one {:!important;}',
  481. '.one {;!important}'
  482. );
  483. $cssout = '.one{}';
  484. foreach ($cssin as $css) {
  485. $this->assertSame($cssout, $optimiser->process($css));
  486. }
  487. $cssin = array(
  488. '.one{background-color:red;}',
  489. '.one {background-color:red;} .one {background-color:}',
  490. '.one {background-color:red;} .one {background-color;}',
  491. '.one {background-color:red;} .one {background-color}',
  492. '.one {background-color:red;} .one {background-color:;}',
  493. '.one {background-color:red;} .one {:blue;}',
  494. '.one {background-color:red;} .one {:#00F}',
  495. );
  496. $cssout = '.one{background-color:#F00;}';
  497. foreach ($cssin as $css) {
  498. $this->assertSame($cssout, $optimiser->process($css));
  499. }
  500. $cssin = '..one {background-color:color:red}';
  501. $cssout = '..one{background-color:color:red;}';
  502. $this->assertSame($cssout, $optimiser->process($cssin));
  503. $cssin = '#.one {background-color:color:red}';
  504. $cssout = '#.one{background-color:color:red;}';
  505. $this->assertSame($cssout, $optimiser->process($cssin));
  506. $cssin = '##one {background-color:color:red}';
  507. $cssout = '##one{background-color:color:red;}';
  508. $this->assertSame($cssout, $optimiser->process($cssin));
  509. $cssin = '.one {background-color:color:red}';
  510. $cssout = '.one{background-color:color:red;}';
  511. $this->assertSame($cssout, $optimiser->process($cssin));
  512. $cssin = '.one {background-color:red;color;border-color:blue}';
  513. $cssout = '.one{background-color:#F00;border-color:#00F;}';
  514. $this->assertSame($cssout, $optimiser->process($cssin));
  515. $cssin = '{background-color:#123456;color:red;}{color:green;}';
  516. $cssout = "{background-color:#123456;color:#008000;}";
  517. $this->assertSame($cssout, $optimiser->process($cssin));
  518. $cssin = '.one {color:red;} {color:green;} .one {background-color:blue;}';
  519. $cssout = ".one{color:#F00;background-color:#00F;} {color:#008000;}";
  520. $this->assertSame($cssout, $optimiser->process($cssin));
  521. }
  522. /**
  523. * Try to break some things.
  524. */
  525. public function test_break_things() {
  526. $optimiser = new css_optimiser();
  527. // Wildcard test.
  528. $cssin = '* {color: black;}';
  529. $cssout = '*{color:#000;}';
  530. $this->assertSame($cssout, $optimiser->process($cssin));
  531. // Wildcard test.
  532. $cssin = '.one * {color: black;}';
  533. $cssout = '.one *{color:#000;}';
  534. $this->assertSame($cssout, $optimiser->process($cssin));
  535. // Wildcard test.
  536. $cssin = '* .one * {color: black;}';
  537. $cssout = '* .one *{color:#000;}';
  538. $this->assertSame($cssout, $optimiser->process($cssin));
  539. // Wildcard test.
  540. $cssin = '*,* {color: black;}';
  541. $cssout = '*{color:#000;}';
  542. $this->assertSame($cssout, $optimiser->process($cssin));
  543. // Wildcard test.
  544. $cssin = '*, * .one {color: black;}';
  545. $cssout = "*, * .one{color:#000;}";
  546. $this->assertSame($cssout, $optimiser->process($cssin));
  547. // Wildcard test.
  548. $cssin = '*, *.one {color: black;}';
  549. $cssout = "*, *.one{color:#000;}";
  550. $this->assertSame($cssout, $optimiser->process($cssin));
  551. // Psedo test.
  552. $cssin = '.one:before {color: black;}';
  553. $cssout = '.one:before{color:#000;}';
  554. $this->assertSame($cssout, $optimiser->process($cssin));
  555. // Psedo test.
  556. $cssin = '.one:after {color: black;}';
  557. $cssout = '.one:after{color:#000;}';
  558. $this->assertSame($cssout, $optimiser->process($cssin));
  559. // Psedo test.
  560. $cssin = '.one:onclick {color: black;}';
  561. $cssout = '.one:onclick{color:#000;}';
  562. $this->assertSame($cssout, $optimiser->process($cssin));
  563. // Test complex CSS rules that don't really exist but mimic other CSS rules.
  564. $cssin = '.one {master-of-destruction: explode(\' \', "What madness");}';
  565. $cssout = '.one{master-of-destruction:explode(\' \', "What madness");}';
  566. $this->assertSame($cssout, $optimiser->process($cssin));
  567. // Test some complex IE css... I couldn't even think of a more complext solution
  568. // than the CSS they came up with.
  569. $cssin = 'a { opacity: 0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); }';
  570. $cssout = 'a{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
  571. $this->assertSame($cssout, $optimiser->process($cssin));
  572. }
  573. /**
  574. * A bulk processing test.
  575. */
  576. public function test_bulk_processing() {
  577. global $CFG;
  578. $cssin = <<<CSS
  579. .test .one {
  580. margin:5px;
  581. border:0;
  582. }
  583. .test .one {
  584. margin: 10px;
  585. color: red;
  586. }
  587. .test.one {
  588. margin: 15px;
  589. }
  590. #test .one {margin: 20px;}
  591. #test #one {margin: 25px;}.test #one {margin: 30px;}
  592. .test .one { background-color: #123; }
  593. .test.one{border:1px solid blue}.test.one{border-color:green;}
  594. @media print {
  595. #test .one {margin: 35px;}
  596. }
  597. @media print {
  598. #test .one {margin: 40px;color: #123456;}
  599. #test #one {margin: 45px;}
  600. }
  601. @media print,screen {
  602. #test .one {color: #654321;}
  603. }
  604. #test .one,
  605. #new.style {color:#000;}
  606. CSS;
  607. $cssout = <<<CSS
  608. .test .one{margin:10px;border-width:0;color:#F00;background-color:#123;}
  609. .test.one{margin:15px;border:1px solid #008000;}
  610. #test .one{margin:20px;color:#000;}
  611. #test #one{margin:25px;}
  612. .test #one{margin:30px;}
  613. #new.style{color:#000;}
  614. @media print {
  615. #test .one{margin:40px;color:#123456;}
  616. #test #one{margin:45px;}
  617. }
  618. @media print,screen {
  619. #test .one{color:#654321;}
  620. }
  621. CSS;
  622. $CFG->cssoptimiserpretty = true;
  623. $optimiser = new css_optimiser();
  624. $this->assertSame($optimiser->process($cssin), $cssout);
  625. unset($CFG->cssoptimiserpretty);
  626. }
  627. /**
  628. * Test CSS colour matching.
  629. */
  630. public function test_css_is_colour() {
  631. // First lets test hex colours.
  632. $this->assertTrue(css_is_colour('#123456'));
  633. $this->assertTrue(css_is_colour('#123'));
  634. $this->assertTrue(css_is_colour('#ABCDEF'));
  635. $this->assertTrue(css_is_colour('#ABC'));
  636. $this->assertTrue(css_is_colour('#abcdef'));
  637. $this->assertTrue(css_is_colour('#abc'));
  638. $this->assertTrue(css_is_colour('#aBcDeF'));
  639. $this->assertTrue(css_is_colour('#aBc'));
  640. $this->assertTrue(css_is_colour('#1a2Bc3'));
  641. $this->assertTrue(css_is_colour('#1Ac'));
  642. // Note the following two colour's aren't really colours but browsers process
  643. // them still.
  644. $this->assertTrue(css_is_colour('#A'));
  645. $this->assertTrue(css_is_colour('#12'));
  646. // Having four or five characters however are not valid colours and
  647. // browsers don't parse them. They need to fail so that broken CSS
  648. // stays broken after optimisation.
  649. $this->assertFalse(css_is_colour('#1234'));
  650. $this->assertFalse(css_is_colour('#12345'));
  651. $this->assertFalse(css_is_colour('#BCDEFG'));
  652. $this->assertFalse(css_is_colour('#'));
  653. $this->assertFalse(css_is_colour('#0000000'));
  654. $this->assertFalse(css_is_colour('#132-245'));
  655. $this->assertFalse(css_is_colour('#13 23 43'));
  656. $this->assertFalse(css_is_colour('123456'));
  657. // Next lets test real browser mapped colours.
  658. $this->assertTrue(css_is_colour('black'));
  659. $this->assertTrue(css_is_colour('blue'));
  660. $this->assertTrue(css_is_colour('BLACK'));
  661. $this->assertTrue(css_is_colour('Black'));
  662. $this->assertTrue(css_is_colour('bLACK'));
  663. $this->assertTrue(css_is_colour('mediumaquamarine'));
  664. $this->assertTrue(css_is_colour('mediumAquamarine'));
  665. $this->assertFalse(css_is_colour('monkey'));
  666. $this->assertFalse(css_is_colour(''));
  667. $this->assertFalse(css_is_colour('not a colour'));
  668. // Next lets test rgb(a) colours.
  669. $this->assertTrue(css_is_colour('rgb(255,255,255)'));
  670. $this->assertTrue(css_is_colour('rgb(0, 0, 0)'));
  671. $this->assertTrue(css_is_colour('RGB (255, 255 , 255)'));
  672. $this->assertTrue(css_is_colour('rgba(0,0,0,0)'));
  673. $this->assertTrue(css_is_colour('RGBA(255,255,255,1)'));
  674. $this->assertTrue(css_is_colour('rgbA(255,255,255,0.5)'));
  675. $this->assertFalse(css_is_colour('rgb(-255,-255,-255)'));
  676. $this->assertFalse(css_is_colour('rgb(256,-256,256)'));
  677. // Now lets test HSL colours.
  678. $this->assertTrue(css_is_colour('hsl(0,0%,100%)'));
  679. $this->assertTrue(css_is_colour('hsl(180, 0%, 10%)'));
  680. $this->assertTrue(css_is_colour('hsl (360, 100% , 95%)'));
  681. // Finally test the special values.
  682. $this->assertTrue(css_is_colour('inherit'));
  683. }
  684. /**
  685. * Test the css_is_width function.
  686. */
  687. public function test_css_is_width() {
  688. $this->assertTrue(css_is_width('0'));
  689. $this->assertTrue(css_is_width('0px'));
  690. $this->assertTrue(css_is_width('0em'));
  691. $this->assertTrue(css_is_width('199px'));
  692. $this->assertTrue(css_is_width('199em'));
  693. $this->assertTrue(css_is_width('199%'));
  694. $this->assertTrue(css_is_width('-1px'));
  695. $this->assertTrue(css_is_width('auto'));
  696. $this->assertTrue(css_is_width('inherit'));
  697. // Valid widths but missing their unit specifier.
  698. $this->assertFalse(css_is_width('0.75'));
  699. $this->assertFalse(css_is_width('3'));
  700. $this->assertFalse(css_is_width('-1'));
  701. // Totally invalid widths.
  702. $this->assertFalse(css_is_width('-'));
  703. $this->assertFalse(css_is_width('bananas'));
  704. $this->assertFalse(css_is_width(''));
  705. $this->assertFalse(css_is_width('top'));
  706. }
  707. /**
  708. * This function tests some of the broken crazy CSS we have in Moodle.
  709. * For each of these things the value needs to be corrected if we can be 100%
  710. * certain what is going wrong, Or it needs to be left as is.
  711. */
  712. public function test_broken_css_found_in_moodle() {
  713. $optimiser = new css_optimiser();
  714. // Notice how things are out of order here but that they get corrected.
  715. $cssin = '.test {background:url([[pix:theme|pageheaderbgred]]) top center no-repeat}';
  716. $cssout = '.test{background:url([[pix:theme|pageheaderbgred]]) no-repeat top center;}';
  717. $this->assertSame($cssout, $optimiser->process($cssin));
  718. // Cursor hand isn't valid.
  719. $cssin = '.test {cursor: hand;}';
  720. $cssout = '.test{cursor:hand;}';
  721. $this->assertSame($cssout, $optimiser->process($cssin));
  722. // Zoom property isn't valid.
  723. $cssin = '.test {zoom: 1;}';
  724. $cssout = '.test{zoom:1;}';
  725. $this->assertSame($cssout, $optimiser->process($cssin));
  726. // Left isn't a valid position property.
  727. $cssin = '.test {position: left;}';
  728. $cssout = '.test{position:left;}';
  729. $this->assertSame($cssout, $optimiser->process($cssin));
  730. // The dark red color isn't a valid HTML color but has a standardised
  731. // translation of #8B0000.
  732. $cssin = '.test {color: darkred;}';
  733. $cssout = '.test{color:#8B0000;}';
  734. $this->assertSame($cssout, $optimiser->process($cssin));
  735. // You can't use argb colours as border colors.
  736. $cssin = '.test {border-bottom: 1px solid rgba(0,0,0,0.25);}';
  737. $cssout = '.test{border-bottom:1px solid rgba(0,0,0,0.25);}';
  738. $this->assertSame($cssout, $optimiser->process($cssin));
  739. // Opacity with annoying IE equivalents....
  740. $cssin = '.test {opacity: 0.5; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50);}';
  741. $cssout = '.test{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
  742. $this->assertSame($cssout, $optimiser->process($cssin));
  743. }
  744. /**
  745. * Test keyframe declarations.
  746. */
  747. public function test_keyframe_css_animation() {
  748. global $CFG;
  749. $optimiser = new css_optimiser();
  750. $css = '.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url(\'[[pix:theme|fp/dnd_arrow]]\') no-repeat center;margin-left:-28px;}';
  751. $this->assertSame($css, $optimiser->process($css));
  752. $css = '@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}';
  753. $this->assertSame($css, $optimiser->process($css));
  754. $css = "@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
  755. $css .= "@-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
  756. $css .= "@-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}";
  757. $this->assertSame($css, $optimiser->process($css));
  758. $cssin = <<<CSS
  759. .test {color:#FFF;}
  760. .testtwo {color:#FFF;}
  761. @media print {
  762. .test {background-color:#FFF;}
  763. }
  764. .dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
  765. @media print {
  766. .test {background-color:#000;}
  767. }
  768. @keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
  769. @-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
  770. @-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
  771. @media print {
  772. .test {background-color:#333;}
  773. }
  774. .test {color:#888;}
  775. .testtwo {color:#888;}
  776. CSS;
  777. $cssout = <<<CSS
  778. .test,
  779. .testtwo{color:#888;}
  780. .dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
  781. @media print {
  782. .test{background-color:#333;}
  783. }
  784. @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  785. @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  786. @-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  787. CSS;
  788. $CFG->cssoptimiserpretty = true;
  789. $this->assertSame($cssout, $optimiser->process($cssin));
  790. unset($CFG->cssoptimiserpretty);
  791. $cssin = <<<CSS
  792. .dndupload-target {display:none;}
  793. .dndsupported .dndupload-ready .dndupload-target {display:block;}
  794. .dndupload-uploadinprogress {display:none;text-align:center;}
  795. .dndupload-uploading .dndupload-uploadinprogress {display:block;}
  796. .dndupload-arrow {background:url('[[pix:theme|fp/dnd_arrow]]') center no-repeat;width:56px;height:47px;position:absolute;margin-left: -28px;/*right:46%;left:46%;*/animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
  797. @keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
  798. /*
  799. * Select Dialogue (File Manager only)
  800. */
  801. .filemanager.fp-select .fp-select-loading {display:none;}
  802. .filemanager.fp-select.loading .fp-select-loading {display:block;}
  803. .filemanager.fp-select.loading form {display:none;}
  804. CSS;
  805. $cssout = <<<CSS
  806. .dndupload-target{display:none;}
  807. .dndsupported .dndupload-ready .dndupload-target{display:block;}
  808. .dndupload-uploadinprogress{display:none;text-align:center;}
  809. .dndupload-uploading .dndupload-uploadinprogress{display:block;}
  810. .dndupload-arrow{background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;width:56px;height:47px;position:absolute;margin-left:-28px;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
  811. .filemanager.fp-select .fp-select-loading{display:none;}
  812. .filemanager.fp-select.loading .fp-select-loading{display:block;}
  813. .filemanager.fp-select.loading form{display:none;}
  814. @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  815. @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  816. @-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
  817. CSS;
  818. $CFG->cssoptimiserpretty = true;
  819. $this->assertSame($cssout, $optimiser->process($cssin));
  820. unset($CFG->cssoptimiserpretty);
  821. }
  822. /**
  823. * Test media declarations.
  824. */
  825. public function test_media_rules() {
  826. $optimiser = new css_optimiser();
  827. $cssin = "@media print {\n .test{background-color:#333;}\n}";
  828. $cssout = "@media print { .test{background-color:#333;} }";
  829. $this->assertSame($cssout, $optimiser->process($cssin));
  830. $cssin = "@media screen and (min-width:30px) {\n #region-main-box{left: 30px;float: left;}\n}";
  831. $cssout = "@media screen and (min-width:30px) { #region-main-box{left:30px;float:left;} }";
  832. $this->assertSame($cssout, $optimiser->process($cssin));
  833. $cssin = "@media all and (min-width:500px) {\n #region-main-box{left:30px;float:left;}\n}";
  834. $cssout = "@media all and (min-width:500px) { #region-main-box{left:30px;float:left;} }";
  835. $this->assertSame($cssout, $optimiser->process($cssin));
  836. $cssin = "@media (min-width:500px) {\n #region-main-box{left:30px;float:left;}\n}";
  837. $cssout = "@media (min-width:500px) { #region-main-box{left:30px;float:left;} }";
  838. $this->assertSame($cssout, $optimiser->process($cssin));
  839. $cssin = "@media screen and (color), projection and (color) {\n #region-main-box{left:30px;float:left;}\n}";
  840. $cssout = "@media screen and (color),projection and (color) { #region-main-box{left:30px;float:left;} }";
  841. $this->assertSame($cssout, $optimiser->process($cssin));
  842. $cssin = "@media print {\n .test{background-color:#000;}\n}@media print {\n .test{background-color:#FFF;}\n}";
  843. $cssout = "@media print { .test{background-color:#FFF;} }";
  844. $this->assertSame($cssout, $optimiser->process($cssin));
  845. $cssin = "@media screen and (min-width:30px) {\n #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:30px) {\n #region-main-box{background-color:#FFF;}\n}";
  846. $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#FFF;} }";
  847. $this->assertSame($cssout, $optimiser->process($cssin));
  848. $cssin = "@media screen and (min-width:30px) {\n #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:31px) {\n #region-main-box{background-color:#FFF;}\n}";
  849. $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#000;} }\n@media screen and (min-width:31px) { #region-main-box{background-color:#FFF;} }";
  850. $this->assertSame($cssout, $optimiser->process($cssin));
  851. $cssin = "@media (min-width: 768px) and (max-width: 979px) {\n*{*zoom:1;}}";
  852. $cssout = "@media (min-width: 768px) and (max-width: 979px) { *{*zoom:1;} }";
  853. $this->assertSame($cssout, $optimiser->process($cssin));
  854. $cssin = "#test {min-width:1200px;}@media (min-width: 768px) {#test {min-width: 1024px;}}";
  855. $cssout = "#test{min-width:1200px;} \n@media (min-width: 768px) { #test{min-width:1024px;} }";
  856. $this->assertSame($cssout, $optimiser->process($cssin));
  857. $cssin = "@media(min-width:768px){#page-calender-view .container fluid{min-width:1024px}}.section_add_menus{text-align:right}";
  858. $cssout = ".section_add_menus{text-align:right;} \n@media (min-width:768px) { #page-calender-view .container fluid{min-width:1024px;} }";
  859. $this->assertSame($cssout, $optimiser->process($cssin));
  860. $cssin = "@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}";
  861. $cssout = "@-ms-keyframes progress-bar-stripes {from{background-position:40px 0;}to{background-position:0 0;}}";
  862. $this->assertSame($cssout, $optimiser->process($cssin));
  863. }
  864. /**
  865. * Test the ordering of CSS optimisationss
  866. */
  867. public function test_css_optimisation_ordering() {
  868. $optimiser = $this->get_optimiser();
  869. $css = '.test{display:none;} .dialogue{display:block;} .dialogue-hidden{display:none;}';
  870. $this->assertSame($css, $optimiser->process($css));
  871. $cssin = '.test{display:none;} .dialogue-hidden{display:none;} .dialogue{display:block;}';
  872. $cssout = '.test, .dialogue-hidden{display:none;} .dialogue{display:block;}';
  873. $this->assertSame($cssout, $optimiser->process($cssin));
  874. }
  875. /**
  876. * Test CSS chunking
  877. */
  878. public function test_css_chunking() {
  879. // Test with an even number of styles.
  880. $css = 'a{}b{}c{}d{}e{}f{}';
  881. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  882. $this->assertInternalType('array', $chunks);
  883. $this->assertCount(3, $chunks);
  884. $this->assertArrayHasKey(0, $chunks);
  885. $this->assertArrayHasKey(1, $chunks);
  886. $this->assertArrayHasKey(2, $chunks);
  887. $this->assertSame('a{}b{}', $chunks[0]);
  888. $this->assertSame('c{}d{}', $chunks[1]);
  889. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne{}f{}", $chunks[2]);
  890. // Test with an odd number of styles.
  891. $css = 'a{}b{}c{}d{}e{}';
  892. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  893. $this->assertInternalType('array', $chunks);
  894. $this->assertCount(3, $chunks);
  895. $this->assertArrayHasKey(0, $chunks);
  896. $this->assertArrayHasKey(1, $chunks);
  897. $this->assertArrayHasKey(2, $chunks);
  898. $this->assertSame('a{}b{}', $chunks[0]);
  899. $this->assertSame('c{}d{}', $chunks[1]);
  900. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne{}", $chunks[2]);
  901. // Test well placed commas.
  902. $css = 'a,b{}c,d{}e,f{}';
  903. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  904. $this->assertInternalType('array', $chunks);
  905. $this->assertCount(3, $chunks);
  906. $this->assertArrayHasKey(0, $chunks);
  907. $this->assertArrayHasKey(1, $chunks);
  908. $this->assertArrayHasKey(2, $chunks);
  909. $this->assertSame('a,b{}', $chunks[0]);
  910. $this->assertSame('c,d{}', $chunks[1]);
  911. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne,f{}", $chunks[2]);
  912. // Test unfortunately placed commas.
  913. $css = 'a{}b,c{color:red;}d{}e{}f{}';
  914. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  915. $this->assertInternalType('array', $chunks);
  916. $this->assertCount(4, $chunks);
  917. $this->assertArrayHasKey(0, $chunks);
  918. $this->assertArrayHasKey(1, $chunks);
  919. $this->assertArrayHasKey(2, $chunks);
  920. $this->assertArrayHasKey(3, $chunks);
  921. $this->assertSame('a{}', $chunks[0]);
  922. $this->assertSame('b,c{color:red;}', $chunks[1]);
  923. $this->assertSame('d{}e{}', $chunks[2]);
  924. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n@import url(styles.php?type=test&chunk=3);\nf{}", $chunks[3]);
  925. // Test unfortunate CSS.
  926. $css = 'a,b,c,d,e,f{color:red;}';
  927. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2, 0);
  928. $this->assertInternalType('array', $chunks);
  929. $this->assertCount(1, $chunks);
  930. $this->assertArrayHasKey(0, $chunks);
  931. $this->assertSame('a,b,c,d,e,f{color:red;}', $chunks[0]);
  932. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
  933. // Test to make sure invalid CSS isn't totally ruined.
  934. $css = 'a{},,,e{},';
  935. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  936. // Believe it or not we want to care what comes out here as this will be parsed correctly
  937. // by a browser.
  938. $this->assertInternalType('array', $chunks);
  939. $this->assertCount(3, $chunks);
  940. $this->assertArrayHasKey(0, $chunks);
  941. $this->assertArrayHasKey(1, $chunks);
  942. $this->assertArrayHasKey(2, $chunks);
  943. $this->assertSame('a{}', $chunks[0]);
  944. $this->assertSame(',,,e{}', $chunks[1]);
  945. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n,", $chunks[2]);
  946. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
  947. // Test utter crap CSS to make sure we don't loop to our deaths.
  948. $css = 'a,b,c,d,e,f';
  949. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  950. $this->assertInternalType('array', $chunks);
  951. $this->assertCount(1, $chunks);
  952. $this->assertArrayHasKey(0, $chunks);
  953. $this->assertSame($css, $chunks[0]);
  954. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
  955. // Test another death situation to make sure we're invincible.
  956. $css = 'a,,,,,e';
  957. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  958. $this->assertInternalType('array', $chunks);
  959. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 4. Those were ignored.');
  960. // I don't care what the outcome is, I just want to make sure it doesn't die.
  961. // Test media queries.
  962. $css = '@media (min-width: 980px) { .a,.b{} }';
  963. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  964. $this->assertCount(1, $chunks);
  965. $this->assertSame('@media (min-width: 980px) { .a,.b{} }', $chunks[0]);
  966. // Test media queries, with commas.
  967. $css = '.a{} @media (min-width: 700px), handheld and (orientation: landscape) { .b{} }';
  968. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  969. $this->assertCount(1, $chunks);
  970. $this->assertSame($css, $chunks[0]);
  971. // Test special rules.
  972. $css = 'a,b{ background-image: linear-gradient(to bottom, #ffffff, #cccccc);}d,e{}';
  973. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  974. $this->assertCount(2, $chunks);
  975. $this->assertSame('a,b{ background-image: linear-gradient(to bottom, #ffffff, #cccccc);}', $chunks[0]);
  976. $this->assertSame("@import url(styles.php?type=test&chunk=1);\nd,e{}", $chunks[1]);
  977. // Test media queries with too many selectors.
  978. $css = '@media (min-width: 980px) { a,b,c,d{} }';
  979. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  980. $this->assertCount(1, $chunks);
  981. $this->assertSame('@media (min-width: 980px) { a,b,c,d{} }', $chunks[0]);
  982. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 34. Those were ignored.');
  983. // Complex test.
  984. $css = '@media (a) {b{}} c{} d,e{} f,g,h{} i,j{x:a,b,c} k,l{} @media(x){l,m{ y: a,b,c}} n{}';
  985. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 3);
  986. $this->assertCount(6, $chunks);
  987. $this->assertSame('@media (a) {b{}} c{}', $chunks[0]);
  988. $this->assertSame(' d,e{}', $chunks[1]);
  989. $this->assertSame(' f,g,h{}', $chunks[2]);
  990. $this->assertSame(' i,j{x:a,b,c}', $chunks[3]);
  991. $this->assertSame(' k,l{}', $chunks[4]);
  992. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n@import url(styles.php?type=test&chunk=3);\n@import url(styles.php?type=test&chunk=4);\n@import url(styles.php?type=test&chunk=5);\n @media(x){l,m{ y: a,b,c}} n{}", $chunks[5]);
  993. // Multiple offset errors.
  994. $css = 'a,b,c{} d,e,f{}';
  995. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  996. $this->assertCount(2, $chunks);
  997. $this->assertSame('a,b,c{}', $chunks[0]);
  998. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n d,e,f{}", $chunks[1]);
  999. $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6, 14. Those were ignored.');
  1000. // Test the split according to IE.
  1001. $css = str_repeat('a{}', 4100);
  1002. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test');
  1003. $this->assertCount(2, $chunks);
  1004. $this->assertSame(str_repeat('a{}', 4095), $chunks[0]);
  1005. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n" . str_repeat('a{}', 5), $chunks[1]);
  1006. // Test strip out comments.
  1007. $css = ".a {/** a\nb\nc */} /** a\nb\nc */ .b{} /** .c,.d{} */ e{}";
  1008. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  1009. $this->assertCount(2, $chunks);
  1010. $this->assertSame('.a {} .b{}', $chunks[0]);
  1011. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n e{}", $chunks[1]);
  1012. // Test something with unicode characters.
  1013. $css = 'a,b{} nav a:hover:after { content: "↓"; } b{ color:test;}';
  1014. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2, true);
  1015. $this->assertCount(2, $chunks);
  1016. $this->assertSame('a,b{}', $chunks[0]);
  1017. $this->assertSame("@import url(styles.php?type=test&chunk=1);\n nav a:hover:after { content: \"↓\"; } b{ color:test;}", $chunks[1]);
  1018. // Test that if there is broken CSS with too many close brace symbols,
  1019. // media rules after that point are still kept together.
  1020. $mediarule = '@media (width=480) {a{}b{}}';
  1021. $css = 'c{}}' . $mediarule . 'd{}';
  1022. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  1023. $this->assertCount(3, $chunks);
  1024. $this->assertEquals($mediarule, $chunks[1]);
  1025. // Test that this still works even with too many close brace symbols
  1026. // inside a media query (note: that broken media query may be split
  1027. // after the break, but any following ones should not be).
  1028. $brokenmediarule = '@media (width=480) {c{}}d{}}';
  1029. $css = $brokenmediarule . 'e{}' . $mediarule . 'f{}';
  1030. $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
  1031. $this->assertCount(4, $chunks);
  1032. $this->assertEquals($mediarule, $chunks[2]);
  1033. }
  1034. /**
  1035. * Test CSS3.
  1036. */
  1037. public function test_css3() {
  1038. $optimiser = $this->get_optimiser();
  1039. $css = '.test > .test{display:inline-block;}';
  1040. $this->assertSame($css, $optimiser->process($css));
  1041. $css = '*{display:inline-block;}';
  1042. $this->assertSame($css, $optimiser->process($css));
  1043. $css = 'div > *{display:inline-block;}';
  1044. $this->assertSame($css, $optimiser->process($css));
  1045. $css = 'div:nth-child(3){display:inline-block;}';
  1046. $this->assertSame($css, $optimiser->process($css));
  1047. $css = '.test:nth-child(3){display:inline-block;}';
  1048. $this->assertSame($css, $optimiser->process($css));
  1049. $css = '*:nth-child(3){display:inline-block;}';
  1050. $this->assertSame($css, $optimiser->process($css));
  1051. $css = '*[id]{display:inline-block;}';
  1052. $this->assertSame($css, $optimiser->process($css));
  1053. $css = '*[id=blah]{display:inline-block;}';
  1054. $this->assertSame($css, $optimiser->process($css));
  1055. $css = '*[*id=blah]{display:inline-block;}';
  1056. $this->assertSame($css, $optimiser->process($css));
  1057. $css = '*[*id=blah_]{display:inline-block;}';
  1058. $this->assertSame($css, $optimiser->process($css));
  1059. $css = '*[id^=blah*d]{display:inline-block;}';
  1060. $this->assertSame($css, $optimiser->process($css));
  1061. $css = '.test{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}';
  1062. $this->assertSame($css, $optimiser->process($css));
  1063. $css = '#test{box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);}';
  1064. $this->assertSame($css, $optimiser->process($css));
  1065. }
  1066. /**
  1067. * Test browser hacks here.
  1068. */
  1069. public function test_browser_hacks() {
  1070. $optimiser = $this->get_optimiser();
  1071. $css = '#test{*zoom:1;}';
  1072. $this->assertSame($css, $optimiser->process($css));
  1073. $css = '.test{width:75%;*width:76%;}';
  1074. $this->assertSame($css, $optimiser->process($css));
  1075. $css = '#test{*zoom:1;*display:inline;}';
  1076. $this->assertSame($css, $optimiser->process($css));
  1077. $css = '.test{width:75%;*width:76%;width:76%}';
  1078. $this->assertSame('.test{width:76%;*width:76%;}', $optimiser->process($css));
  1079. $css = '.test{width:75%;*width:76%;*width:75%}';
  1080. $this->assertSame('.test{width:75%;*width:75%;}', $optimiser->process($css));
  1081. }
  1082. }