Geen omschrijving

plugin.js 39KB

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