gradient.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. 'use strict';
  2. function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
  3. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  4. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  5. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); }
  6. var OldValue = require('../old-value');
  7. var Value = require('../value');
  8. var utils = require('../utils');
  9. var parser = require('postcss-value-parser');
  10. var range = require('normalize-range');
  11. var isDirection = /top|left|right|bottom/gi;
  12. var Gradient = function (_Value) {
  13. _inherits(Gradient, _Value);
  14. function Gradient() {
  15. var _temp, _this, _ret;
  16. _classCallCheck(this, Gradient);
  17. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  18. args[_key] = arguments[_key];
  19. }
  20. return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Value.call.apply(_Value, [this].concat(args))), _this), Object.defineProperty(_this, 'directions', {
  21. enumerable: true,
  22. writable: true,
  23. value: {
  24. top: 'bottom',
  25. left: 'right',
  26. bottom: 'top',
  27. right: 'left'
  28. }
  29. }), Object.defineProperty(_this, 'oldDirections', {
  30. enumerable: true,
  31. writable: true,
  32. value: {
  33. 'top': 'left bottom, left top',
  34. 'left': 'right top, left top',
  35. 'bottom': 'left top, left bottom',
  36. 'right': 'left top, right top',
  37. 'top right': 'left bottom, right top',
  38. 'top left': 'right bottom, left top',
  39. 'right top': 'left bottom, right top',
  40. 'right bottom': 'left top, right bottom',
  41. 'bottom right': 'left top, right bottom',
  42. 'bottom left': 'right top, left bottom',
  43. 'left top': 'right bottom, left top',
  44. 'left bottom': 'right top, left bottom'
  45. }
  46. }), _temp), _possibleConstructorReturn(_this, _ret);
  47. }
  48. // Direction to replace
  49. // Direction to replace
  50. /**
  51. * Change degrees for webkit prefix
  52. */
  53. Gradient.prototype.replace = function replace(string, prefix) {
  54. var ast = parser(string);
  55. for (var _iterator = ast.nodes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
  56. var _ref;
  57. if (_isArray) {
  58. if (_i >= _iterator.length) break;
  59. _ref = _iterator[_i++];
  60. } else {
  61. _i = _iterator.next();
  62. if (_i.done) break;
  63. _ref = _i.value;
  64. }
  65. var node = _ref;
  66. if (node.type === 'function' && node.value === this.name) {
  67. node.nodes = this.newDirection(node.nodes);
  68. node.nodes = this.normalize(node.nodes);
  69. if (prefix === '-webkit- old') {
  70. var changes = this.oldWebkit(node);
  71. if (!changes) {
  72. return false;
  73. }
  74. } else {
  75. node.nodes = this.convertDirection(node.nodes);
  76. node.value = prefix + node.value;
  77. }
  78. }
  79. }
  80. return ast.toString();
  81. };
  82. /**
  83. * Replace first token
  84. */
  85. Gradient.prototype.replaceFirst = function replaceFirst(params) {
  86. for (var _len2 = arguments.length, words = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  87. words[_key2 - 1] = arguments[_key2];
  88. }
  89. var prefix = words.map(function (i) {
  90. if (i === ' ') {
  91. return { type: 'space', value: i };
  92. } else {
  93. return { type: 'word', value: i };
  94. }
  95. });
  96. return prefix.concat(params.slice(1));
  97. };
  98. /**
  99. * Convert angle unit to deg
  100. */
  101. Gradient.prototype.normalizeUnit = function normalizeUnit(str, full) {
  102. var num = parseFloat(str);
  103. var deg = num / full * 360;
  104. return deg + 'deg';
  105. };
  106. /**
  107. * Normalize angle
  108. */
  109. Gradient.prototype.normalize = function normalize(nodes) {
  110. if (!nodes[0]) return nodes;
  111. if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) {
  112. nodes[0].value = this.normalizeUnit(nodes[0].value, 400);
  113. } else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) {
  114. nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI);
  115. } else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) {
  116. nodes[0].value = this.normalizeUnit(nodes[0].value, 1);
  117. } else if (nodes[0].value.indexOf('deg') !== -1) {
  118. var num = parseFloat(nodes[0].value);
  119. num = range.wrap(0, 360, num);
  120. nodes[0].value = num + 'deg';
  121. }
  122. if (nodes[0].value === '0deg') {
  123. nodes = this.replaceFirst(nodes, 'to', ' ', 'top');
  124. } else if (nodes[0].value === '90deg') {
  125. nodes = this.replaceFirst(nodes, 'to', ' ', 'right');
  126. } else if (nodes[0].value === '180deg') {
  127. nodes = this.replaceFirst(nodes, 'to', ' ', 'bottom');
  128. } else if (nodes[0].value === '270deg') {
  129. nodes = this.replaceFirst(nodes, 'to', ' ', 'left');
  130. }
  131. return nodes;
  132. };
  133. /**
  134. * Replace old direction to new
  135. */
  136. Gradient.prototype.newDirection = function newDirection(params) {
  137. if (params[0].value === 'to') {
  138. return params;
  139. }
  140. isDirection.lastIndex = 0; // reset search index of global regexp
  141. if (!isDirection.test(params[0].value)) {
  142. return params;
  143. }
  144. params.unshift({
  145. type: 'word',
  146. value: 'to'
  147. }, {
  148. type: 'space',
  149. value: ' '
  150. });
  151. for (var i = 2; i < params.length; i++) {
  152. if (params[i].type === 'div') {
  153. break;
  154. }
  155. if (params[i].type === 'word') {
  156. params[i].value = this.revertDirection(params[i].value);
  157. }
  158. }
  159. return params;
  160. };
  161. /**
  162. * Look for at word
  163. */
  164. Gradient.prototype.isRadial = function isRadial(params) {
  165. var state = 'before';
  166. for (var _iterator2 = params, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
  167. var _ref2;
  168. if (_isArray2) {
  169. if (_i2 >= _iterator2.length) break;
  170. _ref2 = _iterator2[_i2++];
  171. } else {
  172. _i2 = _iterator2.next();
  173. if (_i2.done) break;
  174. _ref2 = _i2.value;
  175. }
  176. var param = _ref2;
  177. if (state === 'before' && param.type === 'space') {
  178. state = 'at';
  179. } else if (state === 'at' && param.value === 'at') {
  180. state = 'after';
  181. } else if (state === 'after' && param.type === 'space') {
  182. return true;
  183. } else if (param.type === 'div') {
  184. break;
  185. } else {
  186. state = 'before';
  187. }
  188. }
  189. return false;
  190. };
  191. /**
  192. * Change new direction to old
  193. */
  194. Gradient.prototype.convertDirection = function convertDirection(params) {
  195. if (params.length > 0) {
  196. if (params[0].value === 'to') {
  197. this.fixDirection(params);
  198. } else if (params[0].value.indexOf('deg') !== -1) {
  199. this.fixAngle(params);
  200. } else if (this.isRadial(params)) {
  201. this.fixRadial(params);
  202. }
  203. }
  204. return params;
  205. };
  206. /**
  207. * Replace `to top left` to `bottom right`
  208. */
  209. Gradient.prototype.fixDirection = function fixDirection(params) {
  210. params.splice(0, 2);
  211. for (var _iterator3 = params, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
  212. var _ref3;
  213. if (_isArray3) {
  214. if (_i3 >= _iterator3.length) break;
  215. _ref3 = _iterator3[_i3++];
  216. } else {
  217. _i3 = _iterator3.next();
  218. if (_i3.done) break;
  219. _ref3 = _i3.value;
  220. }
  221. var param = _ref3;
  222. if (param.type === 'div') {
  223. break;
  224. }
  225. if (param.type === 'word') {
  226. param.value = this.revertDirection(param.value);
  227. }
  228. }
  229. };
  230. /**
  231. * Add 90 degrees
  232. */
  233. Gradient.prototype.fixAngle = function fixAngle(params) {
  234. var first = params[0].value;
  235. first = parseFloat(first);
  236. first = Math.abs(450 - first) % 360;
  237. first = this.roundFloat(first, 3);
  238. params[0].value = first + 'deg';
  239. };
  240. /**
  241. * Fix radial direction syntax
  242. */
  243. Gradient.prototype.fixRadial = function fixRadial(params) {
  244. var first = [];
  245. var second = [];
  246. var a = void 0,
  247. b = void 0,
  248. c = void 0,
  249. i = void 0,
  250. next = void 0;
  251. for (i = 0; i < params.length - 2; i++) {
  252. a = params[i];
  253. b = params[i + 1];
  254. c = params[i + 2];
  255. if (a.type === 'space' && b.value === 'at' && c.type === 'space') {
  256. next = i + 3;
  257. break;
  258. } else {
  259. first.push(a);
  260. }
  261. }
  262. var div = void 0;
  263. for (i = next; i < params.length; i++) {
  264. if (params[i].type === 'div') {
  265. div = params[i];
  266. break;
  267. } else {
  268. second.push(params[i]);
  269. }
  270. }
  271. params.splice.apply(params, [0, i].concat(second, [div], first));
  272. };
  273. Gradient.prototype.revertDirection = function revertDirection(word) {
  274. return this.directions[word.toLowerCase()] || word;
  275. };
  276. /**
  277. * Round float and save digits under dot
  278. */
  279. Gradient.prototype.roundFloat = function roundFloat(float, digits) {
  280. return parseFloat(float.toFixed(digits));
  281. };
  282. /**
  283. * Convert to old webkit syntax
  284. */
  285. Gradient.prototype.oldWebkit = function oldWebkit(node) {
  286. var nodes = node.nodes;
  287. var string = parser.stringify(node.nodes);
  288. if (this.name !== 'linear-gradient') {
  289. return false;
  290. }
  291. if (nodes[0] && nodes[0].value.indexOf('deg') !== -1) {
  292. return false;
  293. }
  294. if (string.indexOf('px') !== -1 || string.indexOf('-corner') !== -1 || string.indexOf('-side') !== -1) {
  295. return false;
  296. }
  297. var params = [[]];
  298. for (var _iterator4 = nodes, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
  299. var _ref4;
  300. if (_isArray4) {
  301. if (_i4 >= _iterator4.length) break;
  302. _ref4 = _iterator4[_i4++];
  303. } else {
  304. _i4 = _iterator4.next();
  305. if (_i4.done) break;
  306. _ref4 = _i4.value;
  307. }
  308. var i = _ref4;
  309. params[params.length - 1].push(i);
  310. if (i.type === 'div' && i.value === ',') {
  311. params.push([]);
  312. }
  313. }
  314. this.oldDirection(params);
  315. this.colorStops(params);
  316. node.nodes = [];
  317. for (var _iterator5 = params, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) {
  318. var _ref5;
  319. if (_isArray5) {
  320. if (_i5 >= _iterator5.length) break;
  321. _ref5 = _iterator5[_i5++];
  322. } else {
  323. _i5 = _iterator5.next();
  324. if (_i5.done) break;
  325. _ref5 = _i5.value;
  326. }
  327. var param = _ref5;
  328. node.nodes = node.nodes.concat(param);
  329. }
  330. node.nodes.unshift({ type: 'word', value: 'linear' }, this.cloneDiv(node.nodes));
  331. node.value = '-webkit-gradient';
  332. return true;
  333. };
  334. /**
  335. * Change direction syntax to old webkit
  336. */
  337. Gradient.prototype.oldDirection = function oldDirection(params) {
  338. var div = this.cloneDiv(params[0]);
  339. if (params[0][0].value !== 'to') {
  340. return params.unshift([{ type: 'word', value: this.oldDirections.bottom }, div]);
  341. } else {
  342. var _words = [];
  343. for (var _iterator6 = params[0].slice(2), _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) {
  344. var _ref6;
  345. if (_isArray6) {
  346. if (_i6 >= _iterator6.length) break;
  347. _ref6 = _iterator6[_i6++];
  348. } else {
  349. _i6 = _iterator6.next();
  350. if (_i6.done) break;
  351. _ref6 = _i6.value;
  352. }
  353. var node = _ref6;
  354. if (node.type === 'word') {
  355. _words.push(node.value.toLowerCase());
  356. }
  357. }
  358. _words = _words.join(' ');
  359. var old = this.oldDirections[_words] || _words;
  360. params[0] = [{ type: 'word', value: old }, div];
  361. return params[0];
  362. }
  363. };
  364. /**
  365. * Get div token from exists parameters
  366. */
  367. Gradient.prototype.cloneDiv = function cloneDiv(params) {
  368. for (var _iterator7 = params, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) {
  369. var _ref7;
  370. if (_isArray7) {
  371. if (_i7 >= _iterator7.length) break;
  372. _ref7 = _iterator7[_i7++];
  373. } else {
  374. _i7 = _iterator7.next();
  375. if (_i7.done) break;
  376. _ref7 = _i7.value;
  377. }
  378. var i = _ref7;
  379. if (i.type === 'div' && i.value === ',') {
  380. return i;
  381. }
  382. }
  383. return { type: 'div', value: ',', after: ' ' };
  384. };
  385. /**
  386. * Change colors syntax to old webkit
  387. */
  388. Gradient.prototype.colorStops = function colorStops(params) {
  389. var result = [];
  390. for (var i = 0; i < params.length; i++) {
  391. var pos = void 0;
  392. var param = params[i];
  393. var item = void 0;
  394. if (i === 0) {
  395. continue;
  396. }
  397. var color = parser.stringify(param[0]);
  398. if (param[1] && param[1].type === 'word') {
  399. pos = param[1].value;
  400. } else if (param[2] && param[2].type === 'word') {
  401. pos = param[2].value;
  402. }
  403. var stop = void 0;
  404. if (i === 1 && (!pos || pos === '0%')) {
  405. stop = 'from(' + color + ')';
  406. } else if (i === params.length - 1 && (!pos || pos === '100%')) {
  407. stop = 'to(' + color + ')';
  408. } else if (pos) {
  409. stop = 'color-stop(' + pos + ', ' + color + ')';
  410. } else {
  411. stop = 'color-stop(' + color + ')';
  412. }
  413. var div = param[param.length - 1];
  414. params[i] = [{ type: 'word', value: stop }];
  415. if (div.type === 'div' && div.value === ',') {
  416. item = params[i].push(div);
  417. }
  418. result.push(item);
  419. }
  420. return result;
  421. };
  422. /**
  423. * Remove old WebKit gradient too
  424. */
  425. Gradient.prototype.old = function old(prefix) {
  426. if (prefix === '-webkit-') {
  427. var type = this.name === 'linear-gradient' ? 'linear' : 'radial';
  428. var string = '-gradient';
  429. var regexp = utils.regexp('-webkit-(' + type + '-gradient|gradient\\(\\s*' + type + ')', false);
  430. return new OldValue(this.name, prefix + this.name, string, regexp);
  431. } else {
  432. return _Value.prototype.old.call(this, prefix);
  433. }
  434. };
  435. /**
  436. * Do not add non-webkit prefixes for list-style and object
  437. */
  438. Gradient.prototype.add = function add(decl, prefix) {
  439. var p = decl.prop;
  440. if (p.indexOf('mask') !== -1) {
  441. if (prefix === '-webkit-' || prefix === '-webkit- old') {
  442. return _Value.prototype.add.call(this, decl, prefix);
  443. }
  444. } else if (p === 'list-style' || p === 'list-style-image' || p === 'content') {
  445. if (prefix === '-webkit-' || prefix === '-webkit- old') {
  446. return _Value.prototype.add.call(this, decl, prefix);
  447. }
  448. } else {
  449. return _Value.prototype.add.call(this, decl, prefix);
  450. }
  451. return undefined;
  452. };
  453. return Gradient;
  454. }(Value);
  455. Object.defineProperty(Gradient, 'names', {
  456. enumerable: true,
  457. writable: true,
  458. value: ['linear-gradient', 'repeating-linear-gradient', 'radial-gradient', 'repeating-radial-gradient']
  459. });
  460. module.exports = Gradient;