editor_plugin_src.js 26 KB

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