plugin.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204
  1. (function () {
  2. var image = (function () {
  3. 'use strict';
  4. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  5. var hasDimensions = function (editor) {
  6. return editor.settings.image_dimensions === false ? false : true;
  7. };
  8. var hasAdvTab = function (editor) {
  9. return editor.settings.image_advtab === true ? true : false;
  10. };
  11. var getPrependUrl = function (editor) {
  12. return editor.getParam('image_prepend_url', '');
  13. };
  14. var getClassList = function (editor) {
  15. return editor.getParam('image_class_list');
  16. };
  17. var hasDescription = function (editor) {
  18. return editor.settings.image_description === false ? false : true;
  19. };
  20. var hasImageTitle = function (editor) {
  21. return editor.settings.image_title === true ? true : false;
  22. };
  23. var hasImageCaption = function (editor) {
  24. return editor.settings.image_caption === true ? true : false;
  25. };
  26. var getImageList = function (editor) {
  27. return editor.getParam('image_list', false);
  28. };
  29. var hasUploadUrl = function (editor) {
  30. return editor.getParam('images_upload_url', false);
  31. };
  32. var hasUploadHandler = function (editor) {
  33. return editor.getParam('images_upload_handler', false);
  34. };
  35. var getUploadUrl = function (editor) {
  36. return editor.getParam('images_upload_url');
  37. };
  38. var getUploadHandler = function (editor) {
  39. return editor.getParam('images_upload_handler');
  40. };
  41. var getUploadBasePath = function (editor) {
  42. return editor.getParam('images_upload_base_path');
  43. };
  44. var getUploadCredentials = function (editor) {
  45. return editor.getParam('images_upload_credentials');
  46. };
  47. var Settings = {
  48. hasDimensions: hasDimensions,
  49. hasAdvTab: hasAdvTab,
  50. getPrependUrl: getPrependUrl,
  51. getClassList: getClassList,
  52. hasDescription: hasDescription,
  53. hasImageTitle: hasImageTitle,
  54. hasImageCaption: hasImageCaption,
  55. getImageList: getImageList,
  56. hasUploadUrl: hasUploadUrl,
  57. hasUploadHandler: hasUploadHandler,
  58. getUploadUrl: getUploadUrl,
  59. getUploadHandler: getUploadHandler,
  60. getUploadBasePath: getUploadBasePath,
  61. getUploadCredentials: getUploadCredentials
  62. };
  63. var Global = typeof window !== 'undefined' ? window : Function('return this;')();
  64. var path = function (parts, scope) {
  65. var o = scope !== undefined && scope !== null ? scope : Global;
  66. for (var i = 0; i < parts.length && o !== undefined && o !== null; ++i)
  67. o = o[parts[i]];
  68. return o;
  69. };
  70. var resolve = function (p, scope) {
  71. var parts = p.split('.');
  72. return path(parts, scope);
  73. };
  74. var unsafe = function (name, scope) {
  75. return resolve(name, scope);
  76. };
  77. var getOrDie = function (name, scope) {
  78. var actual = unsafe(name, scope);
  79. if (actual === undefined || actual === null)
  80. throw name + ' not available on this browser';
  81. return actual;
  82. };
  83. var Global$1 = { getOrDie: getOrDie };
  84. function FileReader () {
  85. var f = Global$1.getOrDie('FileReader');
  86. return new f();
  87. }
  88. var global$1 = tinymce.util.Tools.resolve('tinymce.util.Promise');
  89. var global$2 = tinymce.util.Tools.resolve('tinymce.util.Tools');
  90. var global$3 = tinymce.util.Tools.resolve('tinymce.util.XHR');
  91. var parseIntAndGetMax = function (val1, val2) {
  92. return Math.max(parseInt(val1, 10), parseInt(val2, 10));
  93. };
  94. var getImageSize = function (url, callback) {
  95. var img = document.createElement('img');
  96. function done(width, height) {
  97. if (img.parentNode) {
  98. img.parentNode.removeChild(img);
  99. }
  100. callback({
  101. width: width,
  102. height: height
  103. });
  104. }
  105. img.onload = function () {
  106. var width = parseIntAndGetMax(img.width, img.clientWidth);
  107. var height = parseIntAndGetMax(img.height, img.clientHeight);
  108. done(width, height);
  109. };
  110. img.onerror = function () {
  111. done(0, 0);
  112. };
  113. var style = img.style;
  114. style.visibility = 'hidden';
  115. style.position = 'fixed';
  116. style.bottom = style.left = '0px';
  117. style.width = style.height = 'auto';
  118. document.body.appendChild(img);
  119. img.src = url;
  120. };
  121. var buildListItems = function (inputList, itemCallback, startItems) {
  122. function appendItems(values, output) {
  123. output = output || [];
  124. global$2.each(values, function (item) {
  125. var menuItem = { text: item.text || item.title };
  126. if (item.menu) {
  127. menuItem.menu = appendItems(item.menu);
  128. } else {
  129. menuItem.value = item.value;
  130. itemCallback(menuItem);
  131. }
  132. output.push(menuItem);
  133. });
  134. return output;
  135. }
  136. return appendItems(inputList, startItems || []);
  137. };
  138. var removePixelSuffix = function (value) {
  139. if (value) {
  140. value = value.replace(/px$/, '');
  141. }
  142. return value;
  143. };
  144. var addPixelSuffix = function (value) {
  145. if (value.length > 0 && /^[0-9]+$/.test(value)) {
  146. value += 'px';
  147. }
  148. return value;
  149. };
  150. var mergeMargins = function (css) {
  151. if (css.margin) {
  152. var splitMargin = css.margin.split(' ');
  153. switch (splitMargin.length) {
  154. case 1:
  155. css['margin-top'] = css['margin-top'] || splitMargin[0];
  156. css['margin-right'] = css['margin-right'] || splitMargin[0];
  157. css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
  158. css['margin-left'] = css['margin-left'] || splitMargin[0];
  159. break;
  160. case 2:
  161. css['margin-top'] = css['margin-top'] || splitMargin[0];
  162. css['margin-right'] = css['margin-right'] || splitMargin[1];
  163. css['margin-bottom'] = css['margin-bottom'] || splitMargin[0];
  164. css['margin-left'] = css['margin-left'] || splitMargin[1];
  165. break;
  166. case 3:
  167. css['margin-top'] = css['margin-top'] || splitMargin[0];
  168. css['margin-right'] = css['margin-right'] || splitMargin[1];
  169. css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
  170. css['margin-left'] = css['margin-left'] || splitMargin[1];
  171. break;
  172. case 4:
  173. css['margin-top'] = css['margin-top'] || splitMargin[0];
  174. css['margin-right'] = css['margin-right'] || splitMargin[1];
  175. css['margin-bottom'] = css['margin-bottom'] || splitMargin[2];
  176. css['margin-left'] = css['margin-left'] || splitMargin[3];
  177. }
  178. delete css.margin;
  179. }
  180. return css;
  181. };
  182. var createImageList = function (editor, callback) {
  183. var imageList = Settings.getImageList(editor);
  184. if (typeof imageList === 'string') {
  185. global$3.send({
  186. url: imageList,
  187. success: function (text) {
  188. callback(JSON.parse(text));
  189. }
  190. });
  191. } else if (typeof imageList === 'function') {
  192. imageList(callback);
  193. } else {
  194. callback(imageList);
  195. }
  196. };
  197. var waitLoadImage = function (editor, data, imgElm) {
  198. function selectImage() {
  199. imgElm.onload = imgElm.onerror = null;
  200. if (editor.selection) {
  201. editor.selection.select(imgElm);
  202. editor.nodeChanged();
  203. }
  204. }
  205. imgElm.onload = function () {
  206. if (!data.width && !data.height && Settings.hasDimensions(editor)) {
  207. editor.dom.setAttribs(imgElm, {
  208. width: imgElm.clientWidth,
  209. height: imgElm.clientHeight
  210. });
  211. }
  212. selectImage();
  213. };
  214. imgElm.onerror = selectImage;
  215. };
  216. var blobToDataUri = function (blob) {
  217. return new global$1(function (resolve, reject) {
  218. var reader = FileReader();
  219. reader.onload = function () {
  220. resolve(reader.result);
  221. };
  222. reader.onerror = function () {
  223. reject(reader.error.message);
  224. };
  225. reader.readAsDataURL(blob);
  226. });
  227. };
  228. var Utils = {
  229. getImageSize: getImageSize,
  230. buildListItems: buildListItems,
  231. removePixelSuffix: removePixelSuffix,
  232. addPixelSuffix: addPixelSuffix,
  233. mergeMargins: mergeMargins,
  234. createImageList: createImageList,
  235. waitLoadImage: waitLoadImage,
  236. blobToDataUri: blobToDataUri
  237. };
  238. var global$4 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
  239. var hasOwnProperty = Object.prototype.hasOwnProperty;
  240. var shallow = function (old, nu) {
  241. return nu;
  242. };
  243. var baseMerge = function (merger) {
  244. return function () {
  245. var objects = new Array(arguments.length);
  246. for (var i = 0; i < objects.length; i++)
  247. objects[i] = arguments[i];
  248. if (objects.length === 0)
  249. throw new Error('Can\'t merge zero objects');
  250. var ret = {};
  251. for (var j = 0; j < objects.length; j++) {
  252. var curObject = objects[j];
  253. for (var key in curObject)
  254. if (hasOwnProperty.call(curObject, key)) {
  255. ret[key] = merger(ret[key], curObject[key]);
  256. }
  257. }
  258. return ret;
  259. };
  260. };
  261. var merge = baseMerge(shallow);
  262. var DOM = global$4.DOM;
  263. var getHspace = function (image) {
  264. if (image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight) {
  265. return Utils.removePixelSuffix(image.style.marginLeft);
  266. } else {
  267. return '';
  268. }
  269. };
  270. var getVspace = function (image) {
  271. if (image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom) {
  272. return Utils.removePixelSuffix(image.style.marginTop);
  273. } else {
  274. return '';
  275. }
  276. };
  277. var getBorder = function (image) {
  278. if (image.style.borderWidth) {
  279. return Utils.removePixelSuffix(image.style.borderWidth);
  280. } else {
  281. return '';
  282. }
  283. };
  284. var getAttrib = function (image, name$$1) {
  285. if (image.hasAttribute(name$$1)) {
  286. return image.getAttribute(name$$1);
  287. } else {
  288. return '';
  289. }
  290. };
  291. var getStyle = function (image, name$$1) {
  292. return image.style[name$$1] ? image.style[name$$1] : '';
  293. };
  294. var hasCaption = function (image) {
  295. return image.parentNode !== null && image.parentNode.nodeName === 'FIGURE';
  296. };
  297. var setAttrib = function (image, name$$1, value) {
  298. image.setAttribute(name$$1, value);
  299. };
  300. var wrapInFigure = function (image) {
  301. var figureElm = DOM.create('figure', { class: 'image' });
  302. DOM.insertAfter(figureElm, image);
  303. figureElm.appendChild(image);
  304. figureElm.appendChild(DOM.create('figcaption', { contentEditable: true }, 'Caption'));
  305. figureElm.contentEditable = 'false';
  306. };
  307. var removeFigure = function (image) {
  308. var figureElm = image.parentNode;
  309. DOM.insertAfter(image, figureElm);
  310. DOM.remove(figureElm);
  311. };
  312. var toggleCaption = function (image) {
  313. if (hasCaption(image)) {
  314. removeFigure(image);
  315. } else {
  316. wrapInFigure(image);
  317. }
  318. };
  319. var normalizeStyle = function (image, normalizeCss) {
  320. var attrValue = image.getAttribute('style');
  321. var value = normalizeCss(attrValue !== null ? attrValue : '');
  322. if (value.length > 0) {
  323. image.setAttribute('style', value);
  324. image.setAttribute('data-mce-style', value);
  325. } else {
  326. image.removeAttribute('style');
  327. }
  328. };
  329. var setSize = function (name$$1, normalizeCss) {
  330. return function (image, name$$1, value) {
  331. if (image.style[name$$1]) {
  332. image.style[name$$1] = Utils.addPixelSuffix(value);
  333. normalizeStyle(image, normalizeCss);
  334. } else {
  335. setAttrib(image, name$$1, value);
  336. }
  337. };
  338. };
  339. var getSize = function (image, name$$1) {
  340. if (image.style[name$$1]) {
  341. return Utils.removePixelSuffix(image.style[name$$1]);
  342. } else {
  343. return getAttrib(image, name$$1);
  344. }
  345. };
  346. var setHspace = function (image, value) {
  347. var pxValue = Utils.addPixelSuffix(value);
  348. image.style.marginLeft = pxValue;
  349. image.style.marginRight = pxValue;
  350. };
  351. var setVspace = function (image, value) {
  352. var pxValue = Utils.addPixelSuffix(value);
  353. image.style.marginTop = pxValue;
  354. image.style.marginBottom = pxValue;
  355. };
  356. var setBorder = function (image, value) {
  357. var pxValue = Utils.addPixelSuffix(value);
  358. image.style.borderWidth = pxValue;
  359. };
  360. var setBorderStyle = function (image, value) {
  361. image.style.borderStyle = value;
  362. };
  363. var getBorderStyle = function (image) {
  364. return getStyle(image, 'borderStyle');
  365. };
  366. var isFigure = function (elm) {
  367. return elm.nodeName === 'FIGURE';
  368. };
  369. var defaultData = function () {
  370. return {
  371. src: '',
  372. alt: '',
  373. title: '',
  374. width: '',
  375. height: '',
  376. class: '',
  377. style: '',
  378. caption: false,
  379. hspace: '',
  380. vspace: '',
  381. border: '',
  382. borderStyle: ''
  383. };
  384. };
  385. var getStyleValue = function (normalizeCss, data) {
  386. var image = document.createElement('img');
  387. setAttrib(image, 'style', data.style);
  388. if (getHspace(image) || data.hspace !== '') {
  389. setHspace(image, data.hspace);
  390. }
  391. if (getVspace(image) || data.vspace !== '') {
  392. setVspace(image, data.vspace);
  393. }
  394. if (getBorder(image) || data.border !== '') {
  395. setBorder(image, data.border);
  396. }
  397. if (getBorderStyle(image) || data.borderStyle !== '') {
  398. setBorderStyle(image, data.borderStyle);
  399. }
  400. return normalizeCss(image.getAttribute('style'));
  401. };
  402. var create = function (normalizeCss, data) {
  403. var image = document.createElement('img');
  404. write(normalizeCss, merge(data, { caption: false }), image);
  405. setAttrib(image, 'alt', data.alt);
  406. if (data.caption) {
  407. var figure = DOM.create('figure', { class: 'image' });
  408. figure.appendChild(image);
  409. figure.appendChild(DOM.create('figcaption', { contentEditable: true }, 'Caption'));
  410. figure.contentEditable = 'false';
  411. return figure;
  412. } else {
  413. return image;
  414. }
  415. };
  416. var read = function (normalizeCss, image) {
  417. return {
  418. src: getAttrib(image, 'src'),
  419. alt: getAttrib(image, 'alt'),
  420. title: getAttrib(image, 'title'),
  421. width: getSize(image, 'width'),
  422. height: getSize(image, 'height'),
  423. class: getAttrib(image, 'class'),
  424. style: normalizeCss(getAttrib(image, 'style')),
  425. caption: hasCaption(image),
  426. hspace: getHspace(image),
  427. vspace: getVspace(image),
  428. border: getBorder(image),
  429. borderStyle: getStyle(image, 'borderStyle')
  430. };
  431. };
  432. var updateProp = function (image, oldData, newData, name$$1, set) {
  433. if (newData[name$$1] !== oldData[name$$1]) {
  434. set(image, name$$1, newData[name$$1]);
  435. }
  436. };
  437. var normalized = function (set, normalizeCss) {
  438. return function (image, name$$1, value) {
  439. set(image, value);
  440. normalizeStyle(image, normalizeCss);
  441. };
  442. };
  443. var write = function (normalizeCss, newData, image) {
  444. var oldData = read(normalizeCss, image);
  445. updateProp(image, oldData, newData, 'caption', function (image, _name, _value) {
  446. return toggleCaption(image);
  447. });
  448. updateProp(image, oldData, newData, 'src', setAttrib);
  449. updateProp(image, oldData, newData, 'alt', setAttrib);
  450. updateProp(image, oldData, newData, 'title', setAttrib);
  451. updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss));
  452. updateProp(image, oldData, newData, 'height', setSize('height', normalizeCss));
  453. updateProp(image, oldData, newData, 'class', setAttrib);
  454. updateProp(image, oldData, newData, 'style', normalized(function (image, value) {
  455. return setAttrib(image, 'style', value);
  456. }, normalizeCss));
  457. updateProp(image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss));
  458. updateProp(image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss));
  459. updateProp(image, oldData, newData, 'border', normalized(setBorder, normalizeCss));
  460. updateProp(image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss));
  461. };
  462. var normalizeCss = function (editor, cssText) {
  463. var css = editor.dom.styles.parse(cssText);
  464. var mergedCss = Utils.mergeMargins(css);
  465. var compressed = editor.dom.styles.parse(editor.dom.styles.serialize(mergedCss));
  466. return editor.dom.styles.serialize(compressed);
  467. };
  468. var getSelectedImage = function (editor) {
  469. var imgElm = editor.selection.getNode();
  470. var figureElm = editor.dom.getParent(imgElm, 'figure.image');
  471. if (figureElm) {
  472. return editor.dom.select('img', figureElm)[0];
  473. }
  474. if (imgElm && (imgElm.nodeName !== 'IMG' || imgElm.getAttribute('data-mce-object') || imgElm.getAttribute('data-mce-placeholder'))) {
  475. return null;
  476. }
  477. return imgElm;
  478. };
  479. var splitTextBlock = function (editor, figure) {
  480. var dom = editor.dom;
  481. var textBlock = dom.getParent(figure.parentNode, function (node) {
  482. return editor.schema.getTextBlockElements()[node.nodeName];
  483. }, editor.getBody());
  484. if (textBlock) {
  485. return dom.split(textBlock, figure);
  486. } else {
  487. return figure;
  488. }
  489. };
  490. var readImageDataFromSelection = function (editor) {
  491. var image = getSelectedImage(editor);
  492. return image ? read(function (css) {
  493. return normalizeCss(editor, css);
  494. }, image) : defaultData();
  495. };
  496. var insertImageAtCaret = function (editor, data) {
  497. var elm = create(function (css) {
  498. return normalizeCss(editor, css);
  499. }, data);
  500. editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew');
  501. editor.focus();
  502. editor.selection.setContent(elm.outerHTML);
  503. var insertedElm = editor.dom.select('*[data-mce-id="__mcenew"]')[0];
  504. editor.dom.setAttrib(insertedElm, 'data-mce-id', null);
  505. if (isFigure(insertedElm)) {
  506. var figure = splitTextBlock(editor, insertedElm);
  507. editor.selection.select(figure);
  508. } else {
  509. editor.selection.select(insertedElm);
  510. }
  511. };
  512. var syncSrcAttr = function (editor, image) {
  513. editor.dom.setAttrib(image, 'src', image.getAttribute('src'));
  514. };
  515. var deleteImage = function (editor, image) {
  516. if (image) {
  517. var elm = editor.dom.is(image.parentNode, 'figure.image') ? image.parentNode : image;
  518. editor.dom.remove(elm);
  519. editor.focus();
  520. editor.nodeChanged();
  521. if (editor.dom.isEmpty(editor.getBody())) {
  522. editor.setContent('');
  523. editor.selection.setCursorLocation();
  524. }
  525. }
  526. };
  527. var writeImageDataToSelection = function (editor, data) {
  528. var image = getSelectedImage(editor);
  529. write(function (css) {
  530. return normalizeCss(editor, css);
  531. }, data, image);
  532. syncSrcAttr(editor, image);
  533. if (isFigure(image.parentNode)) {
  534. var figure = image.parentNode;
  535. splitTextBlock(editor, figure);
  536. editor.selection.select(image.parentNode);
  537. } else {
  538. editor.selection.select(image);
  539. Utils.waitLoadImage(editor, data, image);
  540. }
  541. };
  542. var insertOrUpdateImage = function (editor, data) {
  543. var image = getSelectedImage(editor);
  544. if (image) {
  545. if (data.src) {
  546. writeImageDataToSelection(editor, data);
  547. } else {
  548. deleteImage(editor, image);
  549. }
  550. } else if (data.src) {
  551. insertImageAtCaret(editor, data);
  552. }
  553. };
  554. var updateVSpaceHSpaceBorder = function (editor) {
  555. return function (evt) {
  556. var dom = editor.dom;
  557. var rootControl = evt.control.rootControl;
  558. if (!Settings.hasAdvTab(editor)) {
  559. return;
  560. }
  561. var data = rootControl.toJSON();
  562. var css = dom.parseStyle(data.style);
  563. rootControl.find('#vspace').value('');
  564. rootControl.find('#hspace').value('');
  565. css = Utils.mergeMargins(css);
  566. if (css['margin-top'] && css['margin-bottom'] || css['margin-right'] && css['margin-left']) {
  567. if (css['margin-top'] === css['margin-bottom']) {
  568. rootControl.find('#vspace').value(Utils.removePixelSuffix(css['margin-top']));
  569. } else {
  570. rootControl.find('#vspace').value('');
  571. }
  572. if (css['margin-right'] === css['margin-left']) {
  573. rootControl.find('#hspace').value(Utils.removePixelSuffix(css['margin-right']));
  574. } else {
  575. rootControl.find('#hspace').value('');
  576. }
  577. }
  578. if (css['border-width']) {
  579. rootControl.find('#border').value(Utils.removePixelSuffix(css['border-width']));
  580. } else {
  581. rootControl.find('#border').value('');
  582. }
  583. if (css['border-style']) {
  584. rootControl.find('#borderStyle').value(css['border-style']);
  585. } else {
  586. rootControl.find('#borderStyle').value('');
  587. }
  588. rootControl.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
  589. };
  590. };
  591. var updateStyle = function (editor, win) {
  592. win.find('#style').each(function (ctrl) {
  593. var value = getStyleValue(function (css) {
  594. return normalizeCss(editor, css);
  595. }, merge(defaultData(), win.toJSON()));
  596. ctrl.value(value);
  597. });
  598. };
  599. var makeTab = function (editor) {
  600. return {
  601. title: 'Advanced',
  602. type: 'form',
  603. pack: 'start',
  604. items: [
  605. {
  606. label: 'Style',
  607. name: 'style',
  608. type: 'textbox',
  609. onchange: updateVSpaceHSpaceBorder(editor)
  610. },
  611. {
  612. type: 'form',
  613. layout: 'grid',
  614. packV: 'start',
  615. columns: 2,
  616. padding: 0,
  617. defaults: {
  618. type: 'textbox',
  619. maxWidth: 50,
  620. onchange: function (evt) {
  621. updateStyle(editor, evt.control.rootControl);
  622. }
  623. },
  624. items: [
  625. {
  626. label: 'Vertical space',
  627. name: 'vspace'
  628. },
  629. {
  630. label: 'Border width',
  631. name: 'border'
  632. },
  633. {
  634. label: 'Horizontal space',
  635. name: 'hspace'
  636. },
  637. {
  638. label: 'Border style',
  639. type: 'listbox',
  640. name: 'borderStyle',
  641. width: 90,
  642. maxWidth: 90,
  643. onselect: function (evt) {
  644. updateStyle(editor, evt.control.rootControl);
  645. },
  646. values: [
  647. {
  648. text: 'Select...',
  649. value: ''
  650. },
  651. {
  652. text: 'Solid',
  653. value: 'solid'
  654. },
  655. {
  656. text: 'Dotted',
  657. value: 'dotted'
  658. },
  659. {
  660. text: 'Dashed',
  661. value: 'dashed'
  662. },
  663. {
  664. text: 'Double',
  665. value: 'double'
  666. },
  667. {
  668. text: 'Groove',
  669. value: 'groove'
  670. },
  671. {
  672. text: 'Ridge',
  673. value: 'ridge'
  674. },
  675. {
  676. text: 'Inset',
  677. value: 'inset'
  678. },
  679. {
  680. text: 'Outset',
  681. value: 'outset'
  682. },
  683. {
  684. text: 'None',
  685. value: 'none'
  686. },
  687. {
  688. text: 'Hidden',
  689. value: 'hidden'
  690. }
  691. ]
  692. }
  693. ]
  694. }
  695. ]
  696. };
  697. };
  698. var AdvTab = { makeTab: makeTab };
  699. var doSyncSize = function (widthCtrl, heightCtrl) {
  700. widthCtrl.state.set('oldVal', widthCtrl.value());
  701. heightCtrl.state.set('oldVal', heightCtrl.value());
  702. };
  703. var doSizeControls = function (win, f) {
  704. var widthCtrl = win.find('#width')[0];
  705. var heightCtrl = win.find('#height')[0];
  706. var constrained = win.find('#constrain')[0];
  707. if (widthCtrl && heightCtrl && constrained) {
  708. f(widthCtrl, heightCtrl, constrained.checked());
  709. }
  710. };
  711. var doUpdateSize = function (widthCtrl, heightCtrl, isContrained) {
  712. var oldWidth = widthCtrl.state.get('oldVal');
  713. var oldHeight = heightCtrl.state.get('oldVal');
  714. var newWidth = widthCtrl.value();
  715. var newHeight = heightCtrl.value();
  716. if (isContrained && oldWidth && oldHeight && newWidth && newHeight) {
  717. if (newWidth !== oldWidth) {
  718. newHeight = Math.round(newWidth / oldWidth * newHeight);
  719. if (!isNaN(newHeight)) {
  720. heightCtrl.value(newHeight);
  721. }
  722. } else {
  723. newWidth = Math.round(newHeight / oldHeight * newWidth);
  724. if (!isNaN(newWidth)) {
  725. widthCtrl.value(newWidth);
  726. }
  727. }
  728. }
  729. doSyncSize(widthCtrl, heightCtrl);
  730. };
  731. var syncSize = function (win) {
  732. doSizeControls(win, doSyncSize);
  733. };
  734. var updateSize = function (win) {
  735. doSizeControls(win, doUpdateSize);
  736. };
  737. var createUi = function () {
  738. var recalcSize = function (evt) {
  739. updateSize(evt.control.rootControl);
  740. };
  741. return {
  742. type: 'container',
  743. label: 'Dimensions',
  744. layout: 'flex',
  745. align: 'center',
  746. spacing: 5,
  747. items: [
  748. {
  749. name: 'width',
  750. type: 'textbox',
  751. maxLength: 5,
  752. size: 5,
  753. onchange: recalcSize,
  754. ariaLabel: 'Width'
  755. },
  756. {
  757. type: 'label',
  758. text: 'x'
  759. },
  760. {
  761. name: 'height',
  762. type: 'textbox',
  763. maxLength: 5,
  764. size: 5,
  765. onchange: recalcSize,
  766. ariaLabel: 'Height'
  767. },
  768. {
  769. name: 'constrain',
  770. type: 'checkbox',
  771. checked: true,
  772. text: 'Constrain proportions'
  773. }
  774. ]
  775. };
  776. };
  777. var SizeManager = {
  778. createUi: createUi,
  779. syncSize: syncSize,
  780. updateSize: updateSize
  781. };
  782. var onSrcChange = function (evt, editor) {
  783. var srcURL, prependURL, absoluteURLPattern;
  784. var meta = evt.meta || {};
  785. var control = evt.control;
  786. var rootControl = control.rootControl;
  787. var imageListCtrl = rootControl.find('#image-list')[0];
  788. if (imageListCtrl) {
  789. imageListCtrl.value(editor.convertURL(control.value(), 'src'));
  790. }
  791. global$2.each(meta, function (value, key) {
  792. rootControl.find('#' + key).value(value);
  793. });
  794. if (!meta.width && !meta.height) {
  795. srcURL = editor.convertURL(control.value(), 'src');
  796. prependURL = Settings.getPrependUrl(editor);
  797. absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i');
  798. if (prependURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, prependURL.length) !== prependURL) {
  799. srcURL = prependURL + srcURL;
  800. }
  801. control.value(srcURL);
  802. Utils.getImageSize(editor.documentBaseURI.toAbsolute(control.value()), function (data) {
  803. if (data.width && data.height && Settings.hasDimensions(editor)) {
  804. rootControl.find('#width').value(data.width);
  805. rootControl.find('#height').value(data.height);
  806. SizeManager.syncSize(rootControl);
  807. }
  808. });
  809. }
  810. };
  811. var onBeforeCall = function (evt) {
  812. evt.meta = evt.control.rootControl.toJSON();
  813. };
  814. var getGeneralItems = function (editor, imageListCtrl) {
  815. var generalFormItems = [
  816. {
  817. name: 'src',
  818. type: 'filepicker',
  819. filetype: 'image',
  820. label: 'Source',
  821. autofocus: true,
  822. onchange: function (evt) {
  823. onSrcChange(evt, editor);
  824. },
  825. onbeforecall: onBeforeCall
  826. },
  827. imageListCtrl
  828. ];
  829. if (Settings.hasDescription(editor)) {
  830. generalFormItems.push({
  831. name: 'alt',
  832. type: 'textbox',
  833. label: 'Image description'
  834. });
  835. }
  836. if (Settings.hasImageTitle(editor)) {
  837. generalFormItems.push({
  838. name: 'title',
  839. type: 'textbox',
  840. label: 'Image Title'
  841. });
  842. }
  843. if (Settings.hasDimensions(editor)) {
  844. generalFormItems.push(SizeManager.createUi());
  845. }
  846. if (Settings.getClassList(editor)) {
  847. generalFormItems.push({
  848. name: 'class',
  849. type: 'listbox',
  850. label: 'Class',
  851. values: Utils.buildListItems(Settings.getClassList(editor), function (item) {
  852. if (item.value) {
  853. item.textStyle = function () {
  854. return editor.formatter.getCssText({
  855. inline: 'img',
  856. classes: [item.value]
  857. });
  858. };
  859. }
  860. })
  861. });
  862. }
  863. if (Settings.hasImageCaption(editor)) {
  864. generalFormItems.push({
  865. name: 'caption',
  866. type: 'checkbox',
  867. label: 'Caption'
  868. });
  869. }
  870. return generalFormItems;
  871. };
  872. var makeTab$1 = function (editor, imageListCtrl) {
  873. return {
  874. title: 'General',
  875. type: 'form',
  876. items: getGeneralItems(editor, imageListCtrl)
  877. };
  878. };
  879. var MainTab = {
  880. makeTab: makeTab$1,
  881. getGeneralItems: getGeneralItems
  882. };
  883. var url = function () {
  884. return Global$1.getOrDie('URL');
  885. };
  886. var createObjectURL = function (blob) {
  887. return url().createObjectURL(blob);
  888. };
  889. var revokeObjectURL = function (u) {
  890. url().revokeObjectURL(u);
  891. };
  892. var URL = {
  893. createObjectURL: createObjectURL,
  894. revokeObjectURL: revokeObjectURL
  895. };
  896. var global$5 = tinymce.util.Tools.resolve('tinymce.ui.Factory');
  897. function XMLHttpRequest () {
  898. var f = Global$1.getOrDie('XMLHttpRequest');
  899. return new f();
  900. }
  901. var noop = function () {
  902. };
  903. var pathJoin = function (path1, path2) {
  904. if (path1) {
  905. return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, '');
  906. }
  907. return path2;
  908. };
  909. function Uploader (settings) {
  910. var defaultHandler = function (blobInfo, success, failure, progress) {
  911. var xhr, formData;
  912. xhr = XMLHttpRequest();
  913. xhr.open('POST', settings.url);
  914. xhr.withCredentials = settings.credentials;
  915. xhr.upload.onprogress = function (e) {
  916. progress(e.loaded / e.total * 100);
  917. };
  918. xhr.onerror = function () {
  919. failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
  920. };
  921. xhr.onload = function () {
  922. var json;
  923. if (xhr.status < 200 || xhr.status >= 300) {
  924. failure('HTTP Error: ' + xhr.status);
  925. return;
  926. }
  927. json = JSON.parse(xhr.responseText);
  928. if (!json || typeof json.location !== 'string') {
  929. failure('Invalid JSON: ' + xhr.responseText);
  930. return;
  931. }
  932. success(pathJoin(settings.basePath, json.location));
  933. };
  934. formData = new FormData();
  935. formData.append('file', blobInfo.blob(), blobInfo.filename());
  936. xhr.send(formData);
  937. };
  938. var uploadBlob = function (blobInfo, handler) {
  939. return new global$1(function (resolve, reject) {
  940. try {
  941. handler(blobInfo, resolve, reject, noop);
  942. } catch (ex) {
  943. reject(ex.message);
  944. }
  945. });
  946. };
  947. var isDefaultHandler = function (handler) {
  948. return handler === defaultHandler;
  949. };
  950. var upload = function (blobInfo) {
  951. return !settings.url && isDefaultHandler(settings.handler) ? global$1.reject('Upload url missing from the settings.') : uploadBlob(blobInfo, settings.handler);
  952. };
  953. settings = global$2.extend({
  954. credentials: false,
  955. handler: defaultHandler
  956. }, settings);
  957. return { upload: upload };
  958. }
  959. var onFileInput = function (editor) {
  960. return function (evt) {
  961. var Throbber = global$5.get('Throbber');
  962. var rootControl = evt.control.rootControl;
  963. var throbber = new Throbber(rootControl.getEl());
  964. var file = evt.control.value();
  965. var blobUri = URL.createObjectURL(file);
  966. var uploader = Uploader({
  967. url: Settings.getUploadUrl(editor),
  968. basePath: Settings.getUploadBasePath(editor),
  969. credentials: Settings.getUploadCredentials(editor),
  970. handler: Settings.getUploadHandler(editor)
  971. });
  972. var finalize = function () {
  973. throbber.hide();
  974. URL.revokeObjectURL(blobUri);
  975. };
  976. throbber.show();
  977. return Utils.blobToDataUri(file).then(function (dataUrl) {
  978. var blobInfo = editor.editorUpload.blobCache.create({
  979. blob: file,
  980. blobUri: blobUri,
  981. name: file.name ? file.name.replace(/\.[^\.]+$/, '') : null,
  982. base64: dataUrl.split(',')[1]
  983. });
  984. return uploader.upload(blobInfo).then(function (url) {
  985. var src = rootControl.find('#src');
  986. src.value(url);
  987. rootControl.find('tabpanel')[0].activateTab(0);
  988. src.fire('change');
  989. finalize();
  990. return url;
  991. });
  992. }).catch(function (err) {
  993. editor.windowManager.alert(err);
  994. finalize();
  995. });
  996. };
  997. };
  998. var acceptExts = '.jpg,.jpeg,.png,.gif';
  999. var makeTab$2 = function (editor) {
  1000. return {
  1001. title: 'Upload',
  1002. type: 'form',
  1003. layout: 'flex',
  1004. direction: 'column',
  1005. align: 'stretch',
  1006. padding: '20 20 20 20',
  1007. items: [
  1008. {
  1009. type: 'container',
  1010. layout: 'flex',
  1011. direction: 'column',
  1012. align: 'center',
  1013. spacing: 10,
  1014. items: [
  1015. {
  1016. text: 'Browse for an image',
  1017. type: 'browsebutton',
  1018. accept: acceptExts,
  1019. onchange: onFileInput(editor)
  1020. },
  1021. {
  1022. text: 'OR',
  1023. type: 'label'
  1024. }
  1025. ]
  1026. },
  1027. {
  1028. text: 'Drop an image here',
  1029. type: 'dropzone',
  1030. accept: acceptExts,
  1031. height: 100,
  1032. onchange: onFileInput(editor)
  1033. }
  1034. ]
  1035. };
  1036. };
  1037. var UploadTab = { makeTab: makeTab$2 };
  1038. function curry(fn) {
  1039. var initialArgs = [];
  1040. for (var _i = 1; _i < arguments.length; _i++) {
  1041. initialArgs[_i - 1] = arguments[_i];
  1042. }
  1043. return function () {
  1044. var restArgs = [];
  1045. for (var _i = 0; _i < arguments.length; _i++) {
  1046. restArgs[_i] = arguments[_i];
  1047. }
  1048. var all = initialArgs.concat(restArgs);
  1049. return fn.apply(null, all);
  1050. };
  1051. }
  1052. var submitForm = function (editor, evt) {
  1053. var win = evt.control.getRoot();
  1054. SizeManager.updateSize(win);
  1055. editor.undoManager.transact(function () {
  1056. var data = merge(readImageDataFromSelection(editor), win.toJSON());
  1057. insertOrUpdateImage(editor, data);
  1058. });
  1059. editor.editorUpload.uploadImagesAuto();
  1060. };
  1061. function Dialog (editor) {
  1062. function showDialog(imageList) {
  1063. var data = readImageDataFromSelection(editor);
  1064. var win, imageListCtrl;
  1065. if (imageList) {
  1066. imageListCtrl = {
  1067. type: 'listbox',
  1068. label: 'Image list',
  1069. name: 'image-list',
  1070. values: Utils.buildListItems(imageList, function (item) {
  1071. item.value = editor.convertURL(item.value || item.url, 'src');
  1072. }, [{
  1073. text: 'None',
  1074. value: ''
  1075. }]),
  1076. value: data.src && editor.convertURL(data.src, 'src'),
  1077. onselect: function (e) {
  1078. var altCtrl = win.find('#alt');
  1079. if (!altCtrl.value() || e.lastControl && altCtrl.value() === e.lastControl.text()) {
  1080. altCtrl.value(e.control.text());
  1081. }
  1082. win.find('#src').value(e.control.value()).fire('change');
  1083. },
  1084. onPostRender: function () {
  1085. imageListCtrl = this;
  1086. }
  1087. };
  1088. }
  1089. if (Settings.hasAdvTab(editor) || Settings.hasUploadUrl(editor) || Settings.hasUploadHandler(editor)) {
  1090. var body = [MainTab.makeTab(editor, imageListCtrl)];
  1091. if (Settings.hasAdvTab(editor)) {
  1092. body.push(AdvTab.makeTab(editor));
  1093. }
  1094. if (Settings.hasUploadUrl(editor) || Settings.hasUploadHandler(editor)) {
  1095. body.push(UploadTab.makeTab(editor));
  1096. }
  1097. win = editor.windowManager.open({
  1098. title: 'Insert/edit image',
  1099. data: data,
  1100. bodyType: 'tabpanel',
  1101. body: body,
  1102. onSubmit: curry(submitForm, editor)
  1103. });
  1104. } else {
  1105. win = editor.windowManager.open({
  1106. title: 'Insert/edit image',
  1107. data: data,
  1108. body: MainTab.getGeneralItems(editor, imageListCtrl),
  1109. onSubmit: curry(submitForm, editor)
  1110. });
  1111. }
  1112. SizeManager.syncSize(win);
  1113. }
  1114. function open() {
  1115. Utils.createImageList(editor, showDialog);
  1116. }
  1117. return { open: open };
  1118. }
  1119. var register = function (editor) {
  1120. editor.addCommand('mceImage', Dialog(editor).open);
  1121. };
  1122. var Commands = { register: register };
  1123. var hasImageClass = function (node) {
  1124. var className = node.attr('class');
  1125. return className && /\bimage\b/.test(className);
  1126. };
  1127. var toggleContentEditableState = function (state) {
  1128. return function (nodes) {
  1129. var i = nodes.length, node;
  1130. var toggleContentEditable = function (node) {
  1131. node.attr('contenteditable', state ? 'true' : null);
  1132. };
  1133. while (i--) {
  1134. node = nodes[i];
  1135. if (hasImageClass(node)) {
  1136. node.attr('contenteditable', state ? 'false' : null);
  1137. global$2.each(node.getAll('figcaption'), toggleContentEditable);
  1138. }
  1139. }
  1140. };
  1141. };
  1142. var setup = function (editor) {
  1143. editor.on('preInit', function () {
  1144. editor.parser.addNodeFilter('figure', toggleContentEditableState(true));
  1145. editor.serializer.addNodeFilter('figure', toggleContentEditableState(false));
  1146. });
  1147. };
  1148. var FilterContent = { setup: setup };
  1149. var register$1 = function (editor) {
  1150. editor.addButton('image', {
  1151. icon: 'image',
  1152. tooltip: 'Insert/edit image',
  1153. onclick: Dialog(editor).open,
  1154. stateSelector: 'img:not([data-mce-object],[data-mce-placeholder]),figure.image'
  1155. });
  1156. editor.addMenuItem('image', {
  1157. icon: 'image',
  1158. text: 'Image',
  1159. onclick: Dialog(editor).open,
  1160. context: 'insert',
  1161. prependToContext: true
  1162. });
  1163. };
  1164. var Buttons = { register: register$1 };
  1165. global.add('image', function (editor) {
  1166. FilterContent.setup(editor);
  1167. Buttons.register(editor);
  1168. Commands.register(editor);
  1169. });
  1170. function Plugin () {
  1171. }
  1172. return Plugin;
  1173. }());
  1174. })();