plugin.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  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. })();