plugin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /**
  2. * Copyright (c) Tiny Technologies, Inc. All rights reserved.
  3. * Licensed under the LGPL or a commercial license.
  4. * For LGPL see License.txt in the project root for license information.
  5. * For commercial licenses see https://www.tiny.cloud/
  6. *
  7. * Version: 5.2.0 (2020-02-13)
  8. */
  9. (function () {
  10. 'use strict';
  11. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  12. var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
  13. var global$2 = tinymce.util.Tools.resolve('tinymce.EditorManager');
  14. var global$3 = tinymce.util.Tools.resolve('tinymce.Env');
  15. var global$4 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  16. var shouldMergeClasses = function (editor) {
  17. return editor.getParam('importcss_merge_classes');
  18. };
  19. var shouldImportExclusive = function (editor) {
  20. return editor.getParam('importcss_exclusive');
  21. };
  22. var getSelectorConverter = function (editor) {
  23. return editor.getParam('importcss_selector_converter');
  24. };
  25. var getSelectorFilter = function (editor) {
  26. return editor.getParam('importcss_selector_filter');
  27. };
  28. var getCssGroups = function (editor) {
  29. return editor.getParam('importcss_groups');
  30. };
  31. var shouldAppend = function (editor) {
  32. return editor.getParam('importcss_append');
  33. };
  34. var getFileFilter = function (editor) {
  35. return editor.getParam('importcss_file_filter');
  36. };
  37. var Settings = {
  38. shouldMergeClasses: shouldMergeClasses,
  39. shouldImportExclusive: shouldImportExclusive,
  40. getSelectorConverter: getSelectorConverter,
  41. getSelectorFilter: getSelectorFilter,
  42. getCssGroups: getCssGroups,
  43. shouldAppend: shouldAppend,
  44. getFileFilter: getFileFilter
  45. };
  46. var noop = function () {
  47. };
  48. var constant = function (value) {
  49. return function () {
  50. return value;
  51. };
  52. };
  53. var never = constant(false);
  54. var always = constant(true);
  55. var none = function () {
  56. return NONE;
  57. };
  58. var NONE = function () {
  59. var eq = function (o) {
  60. return o.isNone();
  61. };
  62. var call = function (thunk) {
  63. return thunk();
  64. };
  65. var id = function (n) {
  66. return n;
  67. };
  68. var me = {
  69. fold: function (n, s) {
  70. return n();
  71. },
  72. is: never,
  73. isSome: never,
  74. isNone: always,
  75. getOr: id,
  76. getOrThunk: call,
  77. getOrDie: function (msg) {
  78. throw new Error(msg || 'error: getOrDie called on none.');
  79. },
  80. getOrNull: constant(null),
  81. getOrUndefined: constant(undefined),
  82. or: id,
  83. orThunk: call,
  84. map: none,
  85. each: noop,
  86. bind: none,
  87. exists: never,
  88. forall: always,
  89. filter: none,
  90. equals: eq,
  91. equals_: eq,
  92. toArray: function () {
  93. return [];
  94. },
  95. toString: constant('none()')
  96. };
  97. if (Object.freeze) {
  98. Object.freeze(me);
  99. }
  100. return me;
  101. }();
  102. var typeOf = function (x) {
  103. if (x === null) {
  104. return 'null';
  105. }
  106. var t = typeof x;
  107. if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
  108. return 'array';
  109. }
  110. if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
  111. return 'string';
  112. }
  113. return t;
  114. };
  115. var isType = function (type) {
  116. return function (value) {
  117. return typeOf(value) === type;
  118. };
  119. };
  120. var isArray = isType('array');
  121. var isFunction = isType('function');
  122. var nativeSlice = Array.prototype.slice;
  123. var nativePush = Array.prototype.push;
  124. var map = function (xs, f) {
  125. var len = xs.length;
  126. var r = new Array(len);
  127. for (var i = 0; i < len; i++) {
  128. var x = xs[i];
  129. r[i] = f(x, i);
  130. }
  131. return r;
  132. };
  133. var flatten = function (xs) {
  134. var r = [];
  135. for (var i = 0, len = xs.length; i < len; ++i) {
  136. if (!isArray(xs[i])) {
  137. throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
  138. }
  139. nativePush.apply(r, xs[i]);
  140. }
  141. return r;
  142. };
  143. var bind = function (xs, f) {
  144. return flatten(map(xs, f));
  145. };
  146. var from = isFunction(Array.from) ? Array.from : function (x) {
  147. return nativeSlice.call(x);
  148. };
  149. var generate = function () {
  150. var ungroupedOrder = [];
  151. var groupOrder = [];
  152. var groups = {};
  153. var addItemToGroup = function (groupTitle, itemInfo) {
  154. if (groups[groupTitle]) {
  155. groups[groupTitle].push(itemInfo);
  156. } else {
  157. groupOrder.push(groupTitle);
  158. groups[groupTitle] = [itemInfo];
  159. }
  160. };
  161. var addItem = function (itemInfo) {
  162. ungroupedOrder.push(itemInfo);
  163. };
  164. var toFormats = function () {
  165. var groupItems = bind(groupOrder, function (g) {
  166. var items = groups[g];
  167. return items.length === 0 ? [] : [{
  168. title: g,
  169. items: items
  170. }];
  171. });
  172. return groupItems.concat(ungroupedOrder);
  173. };
  174. return {
  175. addItemToGroup: addItemToGroup,
  176. addItem: addItem,
  177. toFormats: toFormats
  178. };
  179. };
  180. var removeCacheSuffix = function (url) {
  181. var cacheSuffix = global$3.cacheSuffix;
  182. if (typeof url === 'string') {
  183. url = url.replace('?' + cacheSuffix, '').replace('&' + cacheSuffix, '');
  184. }
  185. return url;
  186. };
  187. var isSkinContentCss = function (editor, href) {
  188. var settings = editor.settings, skin = settings.skin !== false ? settings.skin || 'oxide' : false;
  189. if (skin) {
  190. var skinUrl = settings.skin_url ? editor.documentBaseURI.toAbsolute(settings.skin_url) : global$2.baseURL + '/skins/ui/' + skin;
  191. var contentSkinUrlPart = global$2.baseURL + '/skins/content/';
  192. return href === skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css' || href.indexOf(contentSkinUrlPart) !== -1;
  193. }
  194. return false;
  195. };
  196. var compileFilter = function (filter) {
  197. if (typeof filter === 'string') {
  198. return function (value) {
  199. return value.indexOf(filter) !== -1;
  200. };
  201. } else if (filter instanceof RegExp) {
  202. return function (value) {
  203. return filter.test(value);
  204. };
  205. }
  206. return filter;
  207. };
  208. var getSelectors = function (editor, doc, fileFilter) {
  209. var selectors = [], contentCSSUrls = {};
  210. function append(styleSheet, imported) {
  211. var href = styleSheet.href, rules;
  212. href = removeCacheSuffix(href);
  213. if (!href || !fileFilter(href, imported) || isSkinContentCss(editor, href)) {
  214. return;
  215. }
  216. global$4.each(styleSheet.imports, function (styleSheet) {
  217. append(styleSheet, true);
  218. });
  219. try {
  220. rules = styleSheet.cssRules || styleSheet.rules;
  221. } catch (e) {
  222. }
  223. global$4.each(rules, function (cssRule) {
  224. if (cssRule.styleSheet) {
  225. append(cssRule.styleSheet, true);
  226. } else if (cssRule.selectorText) {
  227. global$4.each(cssRule.selectorText.split(','), function (selector) {
  228. selectors.push(global$4.trim(selector));
  229. });
  230. }
  231. });
  232. }
  233. global$4.each(editor.contentCSS, function (url) {
  234. contentCSSUrls[url] = true;
  235. });
  236. if (!fileFilter) {
  237. fileFilter = function (href, imported) {
  238. return imported || contentCSSUrls[href];
  239. };
  240. }
  241. try {
  242. global$4.each(doc.styleSheets, function (styleSheet) {
  243. append(styleSheet);
  244. });
  245. } catch (e) {
  246. }
  247. return selectors;
  248. };
  249. var defaultConvertSelectorToFormat = function (editor, selectorText) {
  250. var format;
  251. var selector = /^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(selectorText);
  252. if (!selector) {
  253. return;
  254. }
  255. var elementName = selector[1];
  256. var classes = selector[2].substr(1).split('.').join(' ');
  257. var inlineSelectorElements = global$4.makeMap('a,img');
  258. if (selector[1]) {
  259. format = { title: selectorText };
  260. if (editor.schema.getTextBlockElements()[elementName]) {
  261. format.block = elementName;
  262. } else if (editor.schema.getBlockElements()[elementName] || inlineSelectorElements[elementName.toLowerCase()]) {
  263. format.selector = elementName;
  264. } else {
  265. format.inline = elementName;
  266. }
  267. } else if (selector[2]) {
  268. format = {
  269. inline: 'span',
  270. title: selectorText.substr(1),
  271. classes: classes
  272. };
  273. }
  274. if (Settings.shouldMergeClasses(editor) !== false) {
  275. format.classes = classes;
  276. } else {
  277. format.attributes = { class: classes };
  278. }
  279. return format;
  280. };
  281. var getGroupsBySelector = function (groups, selector) {
  282. return global$4.grep(groups, function (group) {
  283. return !group.filter || group.filter(selector);
  284. });
  285. };
  286. var compileUserDefinedGroups = function (groups) {
  287. return global$4.map(groups, function (group) {
  288. return global$4.extend({}, group, {
  289. original: group,
  290. selectors: {},
  291. filter: compileFilter(group.filter),
  292. item: {
  293. text: group.title,
  294. menu: []
  295. }
  296. });
  297. });
  298. };
  299. var isExclusiveMode = function (editor, group) {
  300. return group === null || Settings.shouldImportExclusive(editor) !== false;
  301. };
  302. var isUniqueSelector = function (editor, selector, group, globallyUniqueSelectors) {
  303. return !(isExclusiveMode(editor, group) ? selector in globallyUniqueSelectors : selector in group.selectors);
  304. };
  305. var markUniqueSelector = function (editor, selector, group, globallyUniqueSelectors) {
  306. if (isExclusiveMode(editor, group)) {
  307. globallyUniqueSelectors[selector] = true;
  308. } else {
  309. group.selectors[selector] = true;
  310. }
  311. };
  312. var convertSelectorToFormat = function (editor, plugin, selector, group) {
  313. var selectorConverter;
  314. if (group && group.selector_converter) {
  315. selectorConverter = group.selector_converter;
  316. } else if (Settings.getSelectorConverter(editor)) {
  317. selectorConverter = Settings.getSelectorConverter(editor);
  318. } else {
  319. selectorConverter = function () {
  320. return defaultConvertSelectorToFormat(editor, selector);
  321. };
  322. }
  323. return selectorConverter.call(plugin, selector, group);
  324. };
  325. var setup = function (editor) {
  326. editor.on('init', function (e) {
  327. var model = generate();
  328. var globallyUniqueSelectors = {};
  329. var selectorFilter = compileFilter(Settings.getSelectorFilter(editor));
  330. var groups = compileUserDefinedGroups(Settings.getCssGroups(editor));
  331. var processSelector = function (selector, group) {
  332. if (isUniqueSelector(editor, selector, group, globallyUniqueSelectors)) {
  333. markUniqueSelector(editor, selector, group, globallyUniqueSelectors);
  334. var format = convertSelectorToFormat(editor, editor.plugins.importcss, selector, group);
  335. if (format) {
  336. var formatName = format.name || global$1.DOM.uniqueId();
  337. editor.formatter.register(formatName, format);
  338. return global$4.extend({}, {
  339. title: format.title,
  340. format: formatName
  341. });
  342. }
  343. }
  344. return null;
  345. };
  346. global$4.each(getSelectors(editor, editor.getDoc(), compileFilter(Settings.getFileFilter(editor))), function (selector) {
  347. if (selector.indexOf('.mce-') === -1) {
  348. if (!selectorFilter || selectorFilter(selector)) {
  349. var selectorGroups = getGroupsBySelector(groups, selector);
  350. if (selectorGroups.length > 0) {
  351. global$4.each(selectorGroups, function (group) {
  352. var menuItem = processSelector(selector, group);
  353. if (menuItem) {
  354. model.addItemToGroup(group.title, menuItem);
  355. }
  356. });
  357. } else {
  358. var menuItem = processSelector(selector, null);
  359. if (menuItem) {
  360. model.addItem(menuItem);
  361. }
  362. }
  363. }
  364. }
  365. });
  366. var items = model.toFormats();
  367. editor.fire('addStyleModifications', {
  368. items: items,
  369. replace: !Settings.shouldAppend(editor)
  370. });
  371. });
  372. };
  373. var ImportCss = {
  374. defaultConvertSelectorToFormat: defaultConvertSelectorToFormat,
  375. setup: setup
  376. };
  377. var get = function (editor) {
  378. var convertSelectorToFormat = function (selectorText) {
  379. return ImportCss.defaultConvertSelectorToFormat(editor, selectorText);
  380. };
  381. return { convertSelectorToFormat: convertSelectorToFormat };
  382. };
  383. var Api = { get: get };
  384. function Plugin () {
  385. global.add('importcss', function (editor) {
  386. ImportCss.setup(editor);
  387. return Api.get(editor);
  388. });
  389. }
  390. Plugin();
  391. }());