PageRenderTime 62ms CodeModel.GetById 2ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/BlogEngine/BlogEngine.NET/editors/tiny_mce_3_4_3_1/plugins/media/editor_plugin_src.js

#
JavaScript | 777 lines | 535 code | 146 blank | 96 comment | 148 complexity | 0fb95a737ee31355d8f663617b5904b3 MD5 | raw file
  1/**
  2 * editor_plugin_src.js
  3 *
  4 * Copyright 2009, Moxiecode Systems AB
  5 * Released under LGPL License.
  6 *
  7 * License: http://tinymce.moxiecode.com/license
  8 * Contributing: http://tinymce.moxiecode.com/contributing
  9 */
 10
 11(function() {
 12	var rootAttributes = tinymce.explode('id,name,width,height,style,align,class,hspace,vspace,bgcolor,type'), excludedAttrs = tinymce.makeMap(rootAttributes.join(',')), Node = tinymce.html.Node,
 13		mediaTypes, scriptRegExp, JSON = tinymce.util.JSON, mimeTypes;
 14
 15	// Media types supported by this plugin
 16	mediaTypes = [
 17		// Type, clsid:s, mime types, codebase
 18		["Flash", "d27cdb6e-ae6d-11cf-96b8-444553540000", "application/x-shockwave-flash", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
 19		["ShockWave", "166b1bca-3f9c-11cf-8075-444553540000", "application/x-director", "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],
 20		["WindowsMedia", "6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a", "application/x-mplayer2", "http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],
 21		["QuickTime", "02bf25d5-8c17-4b23-bc80-d3488abddc6b", "video/quicktime", "http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],
 22		["RealMedia", "cfcdaa03-8be4-11cf-b84b-0020afbbccfa", "audio/x-pn-realaudio-plugin", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
 23		["Java", "8ad9c840-044e-11d1-b3e9-00805f499d93", "application/x-java-applet", "http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],
 24		["Silverlight", "dfeaf541-f3e1-4c24-acac-99c30715084a", "application/x-silverlight-2"],
 25		["Iframe"],
 26		["Video"]
 27	];
 28
 29	function toArray(obj) {
 30		var undef, out, i;
 31
 32		if (obj && !obj.splice) {
 33			out = [];
 34
 35			for (i = 0; true; i++) {
 36				if (obj[i])
 37					out[i] = obj[i];
 38				else
 39					break;
 40			}
 41
 42			return out;
 43		}
 44
 45		return obj;
 46	};
 47
 48	tinymce.create('tinymce.plugins.MediaPlugin', {
 49		init : function(ed, url) {
 50			var self = this, lookup = {}, i, y, item, name;
 51
 52			function isMediaImg(node) {
 53				return node && node.nodeName === 'IMG' && ed.dom.hasClass(node, 'mceItemMedia');
 54			};
 55
 56			self.editor = ed;
 57			self.url = url;
 58
 59			// Parse media types into a lookup table
 60			scriptRegExp = '';
 61			for (i = 0; i < mediaTypes.length; i++) {
 62				name = mediaTypes[i][0];
 63
 64				item = {
 65					name : name,
 66					clsids : tinymce.explode(mediaTypes[i][1] || ''),
 67					mimes : tinymce.explode(mediaTypes[i][2] || ''),
 68					codebase : mediaTypes[i][3]
 69				};
 70
 71				for (y = 0; y < item.clsids.length; y++)
 72					lookup['clsid:' + item.clsids[y]] = item;
 73
 74				for (y = 0; y < item.mimes.length; y++)
 75					lookup[item.mimes[y]] = item;
 76
 77				lookup['mceItem' + name] = item;
 78				lookup[name.toLowerCase()] = item;
 79
 80				scriptRegExp += (scriptRegExp ? '|' : '') + name;
 81			}
 82
 83			// Handle the media_types setting
 84			tinymce.each(ed.getParam("media_types",
 85				"video=mp4,m4v,ogv,webm;" +
 86				"silverlight=xap;" +
 87				"flash=swf,flv;" +
 88				"shockwave=dcr;" +
 89				"quicktime=mov,qt,mpg,mp3,mpeg;" +
 90				"shockwave=dcr;" +
 91				"windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;" +
 92				"realmedia=rm,ra,ram;" +
 93				"java=jar"
 94			).split(';'), function(item) {
 95				var i, extensions, type;
 96
 97				item = item.split(/=/);
 98				extensions = tinymce.explode(item[1].toLowerCase());
 99				for (i = 0; i < extensions.length; i++) {
100					type = lookup[item[0].toLowerCase()];
101
102					if (type)
103						lookup[extensions[i]] = type;
104				}
105			});
106
107			scriptRegExp = new RegExp('write(' + scriptRegExp + ')\\(([^)]+)\\)');
108			self.lookup = lookup;
109
110			ed.onPreInit.add(function() {
111				// Allow video elements
112				ed.schema.addValidElements('object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]');
113
114				// Convert video elements to image placeholder
115				ed.parser.addNodeFilter('object,embed,video,audio,script,iframe', function(nodes) {
116					var i = nodes.length;
117
118					while (i--)
119						self.objectToImg(nodes[i]);
120				});
121
122				// Convert image placeholders to video elements
123				ed.serializer.addNodeFilter('img', function(nodes, name, args) {
124					var i = nodes.length, node;
125
126					while (i--) {
127						node = nodes[i];
128						if ((node.attr('class') || '').indexOf('mceItemMedia') !== -1)
129							self.imgToObject(node, args);
130					}
131				});
132			});
133
134			ed.onInit.add(function() {
135				// Display "media" instead of "img" in element path
136				if (ed.theme && ed.theme.onResolveName) {
137					ed.theme.onResolveName.add(function(theme, path_object) {
138						if (path_object.name === 'img' && ed.dom.hasClass(path_object.node, 'mceItemMedia'))
139							path_object.name = 'media';
140					});
141				}
142
143				// Add contect menu if it's loaded
144				if (ed && ed.plugins.contextmenu) {
145					ed.plugins.contextmenu.onContextMenu.add(function(plugin, menu, element) {
146						if (element.nodeName === 'IMG' && element.className.indexOf('mceItemMedia') !== -1)
147							menu.add({title : 'media.edit', icon : 'media', cmd : 'mceMedia'});
148					});
149				}
150			});
151
152			// Register commands
153			ed.addCommand('mceMedia', function() {
154				var data, img;
155
156				img = ed.selection.getNode();
157				if (isMediaImg(img)) {
158					data = ed.dom.getAttrib(img, 'data-mce-json');
159					if (data) {
160						data = JSON.parse(data);
161
162						// Add some extra properties to the data object
163						tinymce.each(rootAttributes, function(name) {
164							var value = ed.dom.getAttrib(img, name);
165
166							if (value)
167								data[name] = value;
168						});
169
170						data.type = self.getType(img.className).name.toLowerCase();
171					}
172				}
173
174				if (!data) {
175					data = {
176						type : 'flash',
177						video: {sources:[]},
178						params: {}
179					};
180				}
181
182				ed.windowManager.open({
183					file : url + '/media.htm',
184					width : 430 + parseInt(ed.getLang('media.delta_width', 0)),
185					height : 500 + parseInt(ed.getLang('media.delta_height', 0)),
186					inline : 1
187				}, {
188					plugin_url : url,
189					data : data
190				});
191			});
192
193			// Register buttons
194			ed.addButton('media', {title : 'media.desc', cmd : 'mceMedia'});
195
196			// Update media selection status
197			ed.onNodeChange.add(function(ed, cm, node) {
198				cm.setActive('media', isMediaImg(node));
199			});
200		},
201
202		convertUrl : function(url, force_absolute) {
203			var self = this, editor = self.editor, settings = editor.settings,
204				urlConverter = settings.url_converter,
205				urlConverterScope = settings.url_converter_scope || self;
206
207			if (!url)
208				return url;
209
210			if (force_absolute)
211				return editor.documentBaseURI.toAbsolute(url);
212
213			return urlConverter.call(urlConverterScope, url, 'src', 'object');
214		},
215
216		getInfo : function() {
217			return {
218				longname : 'Media',
219				author : 'Moxiecode Systems AB',
220				authorurl : 'http://tinymce.moxiecode.com',
221				infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media',
222				version : tinymce.majorVersion + "." + tinymce.minorVersion
223			};
224		},
225
226		/**
227		 * Converts the JSON data object to an img node.
228		 */
229		dataToImg : function(data, force_absolute) {
230			var self = this, editor = self.editor, baseUri = editor.documentBaseURI, sources, attrs, img, i;
231
232			data.params.src = self.convertUrl(data.params.src, force_absolute);
233
234			attrs = data.video.attrs;
235			if (attrs)
236				attrs.src = self.convertUrl(attrs.src, force_absolute);
237
238			if (attrs)
239				attrs.poster = self.convertUrl(attrs.poster, force_absolute);
240
241			sources = toArray(data.video.sources);
242			if (sources) {
243				for (i = 0; i < sources.length; i++)
244					sources[i].src = self.convertUrl(sources[i].src, force_absolute);
245			}
246
247			img = self.editor.dom.create('img', {
248				id : data.id,
249				style : data.style,
250				align : data.align,
251				src : self.editor.theme.url + '/img/trans.gif',
252				'class' : 'mceItemMedia mceItem' + self.getType(data.type).name,
253				'data-mce-json' : JSON.serialize(data, "'")
254			});
255
256			img.width = data.width || "320";
257			img.height = data.height || "240";
258
259			return img;
260		},
261
262		/**
263		 * Converts the JSON data object to a HTML string.
264		 */
265		dataToHtml : function(data, force_absolute) {
266			return this.editor.serializer.serialize(this.dataToImg(data, force_absolute), {forced_root_block : '', force_absolute : force_absolute});
267		},
268
269		/**
270		 * Converts the JSON data object to a HTML string.
271		 */
272		htmlToData : function(html) {
273			var fragment, img, data;
274
275			data = {
276				type : 'flash',
277				video: {sources:[]},
278				params: {}
279			};
280
281			fragment = this.editor.parser.parse(html);
282			img = fragment.getAll('img')[0];
283
284			if (img) {
285				data = JSON.parse(img.attr('data-mce-json'));
286				data.type = this.getType(img.attr('class')).name.toLowerCase();
287
288				// Add some extra properties to the data object
289				tinymce.each(rootAttributes, function(name) {
290					var value = img.attr(name);
291
292					if (value)
293						data[name] = value;
294				});
295			}
296
297			return data;
298		},
299
300		/**
301		 * Get type item by extension, class, clsid or mime type.
302		 *
303		 * @method getType
304		 * @param {String} value Value to get type item by.
305		 * @return {Object} Type item object or undefined.
306		 */
307		getType : function(value) {
308			var i, values, typeItem;
309
310			// Find type by checking the classes
311			values = tinymce.explode(value, ' ');
312			for (i = 0; i < values.length; i++) {
313				typeItem = this.lookup[values[i]];
314
315				if (typeItem)
316					return typeItem;
317			}
318		},
319
320		/**
321		 * Converts a tinymce.html.Node image element to video/object/embed.
322		 */
323		imgToObject : function(node, args) {
324			var self = this, editor = self.editor, video, object, embed, iframe, name, value, data,
325				source, sources, params, param, typeItem, i, item, mp4Source, replacement,
326				posterSrc, style;
327
328			// Adds the flash player
329			function addPlayer(video_src, poster_src) {
330				var baseUri, flashVars, flashVarsOutput, params, flashPlayer;
331
332				flashPlayer = editor.getParam('flash_video_player_url', self.convertUrl(self.url + '/moxieplayer.swf'));
333				if (flashPlayer) {
334					baseUri = editor.documentBaseURI;
335					data.params.src = flashPlayer;
336
337					// Convert the movie url to absolute urls
338					if (editor.getParam('flash_video_player_absvideourl', true)) {
339						video_src = baseUri.toAbsolute(video_src || '', true);
340						poster_src = baseUri.toAbsolute(poster_src || '', true);
341					}
342
343					// Generate flash vars
344					flashVarsOutput = '';
345					flashVars = editor.getParam('flash_video_player_flashvars', {url : '$url', poster : '$poster'});
346					tinymce.each(flashVars, function(value, name) {
347						// Replace $url and $poster variables in flashvars value
348						value = value.replace(/\$url/, video_src || '');
349						value = value.replace(/\$poster/, poster_src || '');
350
351						if (value.length > 0)
352							flashVarsOutput += (flashVarsOutput ? '&' : '') + name + '=' + escape(value);
353					});
354
355					if (flashVarsOutput.length)
356						data.params.flashvars = flashVarsOutput;
357
358					params = editor.getParam('flash_video_player_params', {
359						allowfullscreen: true,
360						allowscriptaccess: true
361					});
362
363					tinymce.each(params, function(value, name) {
364						data.params[name] = "" + value;
365					});
366				}
367			};
368
369			data = node.attr('data-mce-json');
370			if (!data)
371				return;
372
373			data = JSON.parse(data);
374			typeItem = this.getType(node.attr('class'));
375
376			style = node.attr('data-mce-style')
377			if (!style) {
378				style = node.attr('style');
379
380				if (style)
381					style = editor.dom.serializeStyle(editor.dom.parseStyle(style, 'img'));
382			}
383
384			// Handle iframe
385			if (typeItem.name === 'Iframe') {
386				replacement = new Node('iframe', 1);
387
388				tinymce.each(rootAttributes, function(name) {
389					var value = node.attr(name);
390
391					if (name == 'class' && value)
392						value = value.replace(/mceItem.+ ?/g, '');
393
394					if (value && value.length > 0)
395						replacement.attr(name, value);
396				});
397
398				for (name in data.params)
399					replacement.attr(name, data.params[name]);
400
401				replacement.attr({
402					style: style,
403					src: data.params.src
404				});
405
406				node.replace(replacement);
407
408				return;
409			}
410
411			// Handle scripts
412			if (this.editor.settings.media_use_script) {
413				replacement = new Node('script', 1).attr('type', 'text/javascript');
414
415				value = new Node('#text', 3);
416				value.value = 'write' + typeItem.name + '(' + JSON.serialize(tinymce.extend(data.params, {
417					width: node.attr('width'),
418					height: node.attr('height')
419				})) + ');';
420
421				replacement.append(value);
422				node.replace(replacement);
423
424				return;
425			}
426
427			// Add HTML5 video element
428			if (typeItem.name === 'Video' && data.video.sources[0]) {
429				// Create new object element
430				video = new Node('video', 1).attr(tinymce.extend({
431					id : node.attr('id'),
432					width: node.attr('width'),
433					height: node.attr('height'),
434					style : style
435				}, data.video.attrs));
436
437				// Get poster source and use that for flash fallback
438				if (data.video.attrs)
439					posterSrc = data.video.attrs.poster;
440
441				sources = data.video.sources = toArray(data.video.sources);
442				for (i = 0; i < sources.length; i++) {
443					if (/\.mp4$/.test(sources[i].src))
444						mp4Source = sources[i].src;
445				}
446
447				if (!sources[0].type) {
448					video.attr('src', sources[0].src);
449					sources.splice(0, 1);
450				}
451
452				for (i = 0; i < sources.length; i++) {
453					source = new Node('source', 1).attr(sources[i]);
454					source.shortEnded = true;
455					video.append(source);
456				}
457
458				// Create flash fallback for video if we have a mp4 source
459				if (mp4Source) {
460					addPlayer(mp4Source, posterSrc);
461					typeItem = self.getType('flash');
462				} else
463					data.params.src = '';
464			}
465
466			// Do we have a params src then we can generate object
467			if (data.params.src) {
468				// Is flv movie add player for it
469				if (/\.flv$/i.test(data.params.src))
470					addPlayer(data.params.src, '');
471
472				if (args && args.force_absolute)
473					data.params.src = editor.documentBaseURI.toAbsolute(data.params.src);
474
475				// Create new object element
476				object = new Node('object', 1).attr({
477					id : node.attr('id'),
478					width: node.attr('width'),
479					height: node.attr('height'),
480					style : style
481				});
482
483				tinymce.each(rootAttributes, function(name) {
484					if (data[name] && name != 'type')
485						object.attr(name, data[name]);
486				});
487
488				// Add params
489				for (name in data.params) {
490					param = new Node('param', 1);
491					param.shortEnded = true;
492					value = data.params[name];
493
494					// Windows media needs to use url instead of src for the media URL
495					if (name === 'src' && typeItem.name === 'WindowsMedia')
496						name = 'url';
497
498					param.attr({name: name, value: value});
499					object.append(param);
500				}
501
502				// Setup add type and classid if strict is disabled
503				if (this.editor.getParam('media_strict', true)) {
504					object.attr({
505						data: data.params.src,
506						type: typeItem.mimes[0]
507					});
508				} else {
509					object.attr({
510						classid: "clsid:" + typeItem.clsids[0],
511						codebase: typeItem.codebase
512					});
513
514					embed = new Node('embed', 1);
515					embed.shortEnded = true;
516					embed.attr({
517						id: node.attr('id'),
518						width: node.attr('width'),
519						height: node.attr('height'),
520						style : style,
521						type: typeItem.mimes[0]
522					});
523
524					for (name in data.params)
525						embed.attr(name, data.params[name]);
526
527					tinymce.each(rootAttributes, function(name) {
528						if (data[name] && name != 'type')
529							embed.attr(name, data[name]);
530					});
531
532					object.append(embed);
533				}
534
535				// Insert raw HTML
536				if (data.object_html) {
537					value = new Node('#text', 3);
538					value.raw = true;
539					value.value = data.object_html;
540					object.append(value);
541				}
542
543				// Append object to video element if it exists
544				if (video)
545					video.append(object);
546			}
547
548			if (video) {
549				// Insert raw HTML
550				if (data.video_html) {
551					value = new Node('#text', 3);
552					value.raw = true;
553					value.value = data.video_html;
554					video.append(value);
555				}
556			}
557
558			if (video || object)
559				node.replace(video || object);
560			else
561				node.remove();
562		},
563
564		/**
565		 * Converts a tinymce.html.Node video/object/embed to an img element.
566		 *
567		 * The video/object/embed will be converted into an image placeholder with a JSON data attribute like this:
568		 * <img class="mceItemMedia mceItemFlash" width="100" height="100" data-mce-json="{..}" />
569		 *
570		 * The JSON structure will be like this:
571		 * {'params':{'flashvars':'something','quality':'high','src':'someurl'}, 'video':{'sources':[{src: 'someurl', type: 'video/mp4'}]}}
572		 */
573		objectToImg : function(node) {
574			var object, embed, video, iframe, img, name, id, width, height, style, i, html,
575				param, params, source, sources, data, type, lookup = this.lookup,
576				matches, attrs, urlConverter = this.editor.settings.url_converter,
577				urlConverterScope = this.editor.settings.url_converter_scope;
578
579			function getInnerHTML(node) {
580				return new tinymce.html.Serializer({
581					inner: true,
582					validate: false
583				}).serialize(node);
584			};
585
586			// If node isn't in document
587			if (!node.parent)
588				return;
589
590			// Handle media scripts
591			if (node.name === 'script') {
592				if (node.firstChild)
593					matches = scriptRegExp.exec(node.firstChild.value);
594
595				if (!matches)
596					return;
597
598				type = matches[1];
599				data = {video : {}, params : JSON.parse(matches[2])};
600				width = data.params.width;
601				height = data.params.height;
602			}
603
604			// Setup data objects
605			data = data || {
606				video : {},
607				params : {}
608			};
609
610			// Setup new image object
611			img = new Node('img', 1);
612			img.attr({
613				src : this.editor.theme.url + '/img/trans.gif'
614			});
615
616			// Video element
617			name = node.name;
618			if (name === 'video') {
619				video = node;
620				object = node.getAll('object')[0];
621				embed = node.getAll('embed')[0];
622				width = video.attr('width');
623				height = video.attr('height');
624				id = video.attr('id');
625				data.video = {attrs : {}, sources : []};
626
627				// Get all video attributes
628				attrs = data.video.attrs;
629				for (name in video.attributes.map)
630					attrs[name] = video.attributes.map[name];
631
632				source = node.attr('src');
633				if (source)
634					data.video.sources.push({src : urlConverter.call(urlConverterScope, source, 'src', 'video')});
635
636				// Get all sources
637				sources = video.getAll("source");
638				for (i = 0; i < sources.length; i++) {
639					source = sources[i].remove();
640
641					data.video.sources.push({
642						src: urlConverter.call(urlConverterScope, source.attr('src'), 'src', 'source'),
643						type: source.attr('type'),
644						media: source.attr('media')
645					});
646				}
647
648				// Convert the poster URL
649				if (attrs.poster)
650					attrs.poster = urlConverter.call(urlConverterScope, attrs.poster, 'poster', 'video');
651			}
652
653			// Object element
654			if (node.name === 'object') {
655				object = node;
656				embed = node.getAll('embed')[0];
657			}
658
659			// Embed element
660			if (node.name === 'embed')
661				embed = node;
662
663			// Iframe element
664			if (node.name === 'iframe') {
665				iframe = node;
666				type = 'Iframe';
667			}
668
669			if (object) {
670				// Get width/height
671				width = width || object.attr('width');
672				height = height || object.attr('height');
673				style = style || object.attr('style');
674				id = id || object.attr('id');
675
676				// Get all object params
677				params = object.getAll("param");
678				for (i = 0; i < params.length; i++) {
679					param = params[i];
680					name = param.remove().attr('name');
681
682					if (!excludedAttrs[name])
683						data.params[name] = param.attr('value');
684				}
685
686				data.params.src = data.params.src || object.attr('data');
687			}
688
689			if (embed) {
690				// Get width/height
691				width = width || embed.attr('width');
692				height = height || embed.attr('height');
693				style = style || embed.attr('style');
694				id = id || embed.attr('id');
695
696				// Get all embed attributes
697				for (name in embed.attributes.map) {
698					if (!excludedAttrs[name] && !data.params[name])
699						data.params[name] = embed.attributes.map[name];
700				}
701			}
702
703			if (iframe) {
704				// Get width/height
705				width = iframe.attr('width');
706				height = iframe.attr('height');
707				style = style || iframe.attr('style');
708				id = iframe.attr('id');
709
710				tinymce.each(rootAttributes, function(name) {
711					img.attr(name, iframe.attr(name));
712				});
713
714				// Get all iframe attributes
715				for (name in iframe.attributes.map) {
716					if (!excludedAttrs[name] && !data.params[name])
717						data.params[name] = iframe.attributes.map[name];
718				}
719			}
720
721			// Use src not movie
722			if (data.params.movie) {
723				data.params.src = data.params.src || data.params.movie;
724				delete data.params.movie;
725			}
726
727			// Convert the URL to relative/absolute depending on configuration
728			if (data.params.src)
729				data.params.src = urlConverter.call(urlConverterScope, data.params.src, 'src', 'object');
730
731			if (video)
732				type = lookup.video.name;
733
734			if (object && !type)
735				type = (lookup[(object.attr('clsid') || '').toLowerCase()] || lookup[(object.attr('type') || '').toLowerCase()] || {}).name;
736
737			if (embed && !type)
738				type = (lookup[(embed.attr('type') || '').toLowerCase()] || {}).name;
739
740			// Replace the video/object/embed element with a placeholder image containing the data
741			node.replace(img);
742
743			// Remove embed
744			if (embed)
745				embed.remove();
746
747			// Serialize the inner HTML of the object element
748			if (object) {
749				html = getInnerHTML(object.remove());
750
751				if (html)
752					data.object_html = html;
753			}
754
755			// Serialize the inner HTML of the video element
756			if (video) {
757				html = getInnerHTML(video.remove());
758
759				if (html)
760					data.video_html = html;
761			}
762
763			// Set width/height of placeholder
764			img.attr({
765				id : id,
766				'class' : 'mceItemMedia mceItem' + (type || 'Flash'),
767				style : style,
768				width : width || "320",
769				height : height || "240",
770				"data-mce-json" : JSON.serialize(data, "'")
771			});
772		}
773	});
774
775	// Register plugin
776	tinymce.PluginManager.add('media', tinymce.plugins.MediaPlugin);
777})();