4 * Copyright, Moxiecode Systems AB
5 * Released under LGPL License.
7 * License: http://www.tinymce.com/license
8 * Contributing: http://www.tinymce.com/contributing
11 /*global tinymce:true */
13 tinymce.PluginManager.add('image', function(editor) {
14 function getImageSize(url, callback) {
15 var img = document.createElement('img');
17 function done(width, height) {
18 img.parentNode.removeChild(img);
19 callback({width: width, height: height});
22 img.onload = function() {
23 done(img.clientWidth, img.clientHeight);
26 img.onerror = function() {
32 var style = img.style;
33 style.visibility = 'hidden';
34 style.position = 'fixed';
35 style.bottom = style.left = 0;
36 style.width = style.height = 'auto';
38 document.body.appendChild(img);
41 function createImageList(callback) {
43 var imageList = editor.settings.image_list;
45 if (typeof(imageList) == "string") {
46 tinymce.util.XHR.send({
48 success: function(text) {
49 callback(tinymce.util.JSON.parse(text));
58 function showDialog(imageList) {
59 var win, data, dom = editor.dom, imgElm = editor.selection.getNode();
60 var width, height, imageListCtrl;
62 function buildImageList() {
63 var linkImageItems = [{text: 'None', value: ''}];
65 tinymce.each(imageList, function(link) {
67 text: link.text || link.title,
68 value: link.value || link.url,
73 return linkImageItems;
76 function recalcSize(e) {
77 var widthCtrl, heightCtrl, newWidth, newHeight;
79 widthCtrl = win.find('#width')[0];
80 heightCtrl = win.find('#height')[0];
82 newWidth = widthCtrl.value();
83 newHeight = heightCtrl.value();
85 if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
86 if (e.control == widthCtrl) {
87 newHeight = Math.round((newWidth / width) * newHeight);
88 heightCtrl.value(newHeight);
90 newWidth = Math.round((newHeight / height) * newWidth);
91 widthCtrl.value(newWidth);
99 function onSubmitForm() {
100 function waitLoad(imgElm) {
101 function selectImage() {
102 imgElm.onload = imgElm.onerror = null;
103 editor.selection.select(imgElm);
104 editor.nodeChanged();
107 imgElm.onload = function() {
108 if (!data.width && !data.height) {
109 dom.setAttribs(imgElm, {
110 width: imgElm.clientWidth,
111 height: imgElm.clientHeight
118 imgElm.onerror = selectImage;
121 var data = win.toJSON();
123 if (data.width === '') {
127 if (data.height === '') {
131 if (data.style === '') {
143 editor.undoManager.transact(function() {
147 editor.nodeChanged();
154 data.id = '__mcenew';
155 editor.selection.setContent(dom.createHTML('img', data));
156 imgElm = dom.get('__mcenew');
157 dom.setAttrib(imgElm, 'id', null);
159 dom.setAttribs(imgElm, data);
166 function removePixelSuffix(value) {
168 value = value.replace(/px$/, '');
174 function updateSize() {
175 getImageSize(this.value(), function(data) {
176 if (data.width && data.height) {
178 height = data.height;
180 win.find('#width').value(width);
181 win.find('#height').value(height);
186 width = dom.getAttrib(imgElm, 'width');
187 height = dom.getAttrib(imgElm, 'height');
189 if (imgElm.nodeName == 'IMG' && !imgElm.getAttribute('data-mce-object')) {
191 src: dom.getAttrib(imgElm, 'src'),
192 alt: dom.getAttrib(imgElm, 'alt'),
205 values: buildImageList(),
206 onselect: function(e) {
207 var altCtrl = win.find('#alt');
209 if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) {
210 altCtrl.value(e.control.text());
213 win.find('#src').value(e.control.value());
218 // General settings shared between simple and advanced dialogs
219 var generalFormItems = [
220 {name: 'src', type: 'filepicker', filetype: 'image', label: 'Source', autofocus: true, onchange: updateSize},
222 {name: 'alt', type: 'textbox', label: 'Image description'},
231 {name: 'width', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
232 {type: 'label', text: 'x'},
233 {name: 'height', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
234 {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
239 function updateStyle() {
240 function addPixelSuffix(value) {
241 if (value.length > 0 && /^[0-9]+$/.test(value)) {
248 var data = win.toJSON();
249 var css = dom.parseStyle(data.style);
251 dom.setAttrib(imgElm, 'style', '');
254 css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace);
255 css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace);
256 css['border-width'] = addPixelSuffix(data.border);
258 win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
261 if (editor.settings.image_advtab) {
262 // Parse styles from img
264 data.hspace = removePixelSuffix(imgElm.style.marginLeft || imgElm.style.marginRight);
265 data.vspace = removePixelSuffix(imgElm.style.marginTop || imgElm.style.marginBottom);
266 data.border = removePixelSuffix(imgElm.style.borderWidth);
267 data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style')));
270 // Advanced dialog shows general+advanced tabs
271 win = editor.windowManager.open({
272 title: 'Insert/edit image',
274 bodyType: 'tabpanel',
279 items: generalFormItems
298 alignH: ['left', 'right'],
302 onchange: updateStyle
305 {label: 'Vertical space', name: 'vspace'},
306 {label: 'Horizontal space', name: 'hspace'},
307 {label: 'Border', name: 'border'}
313 onSubmit: onSubmitForm
316 // Simple default dialog
317 win = editor.windowManager.open({
320 body: generalFormItems,
321 onSubmit: onSubmitForm
326 editor.addButton('image', {
328 tooltip: 'Insert/edit image',
329 onclick: createImageList(showDialog),
330 stateSelector: 'img:not([data-mce-object])'
333 editor.addMenuItem('image', {
335 text: 'Insert image',
336 onclick: createImageList(showDialog),
338 prependToContext: true