plugin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Youtube Embed Plugin
  3. *
  4. * @author Jonnas Fonini <jonnasfonini@gmail.com>
  5. * @version 2.1.8
  6. */
  7. (function () {
  8. CKEDITOR.plugins.add('youtube', {
  9. lang: [ 'en', 'pt', 'pt-br', 'ja', 'hu', 'it', 'fr', 'tr', 'ru', 'de', 'ar', 'nl', 'pl', 'vi', 'zh', 'el', 'he', 'es', 'nb', 'nn', 'fi', 'et', 'sk', 'cs', 'ko', 'eu' ],
  10. init: function (editor) {
  11. editor.addCommand('youtube', new CKEDITOR.dialogCommand('youtube', {
  12. allowedContent: 'div{*}(*); iframe{*}[!width,!height,!src,!frameborder,!allowfullscreen]; object param[*]; a[*]; img[*]'
  13. }));
  14. editor.ui.addButton('Youtube', {
  15. label : editor.lang.youtube.button,
  16. toolbar : 'insert',
  17. command : 'youtube',
  18. icon : this.path + 'images/icon.png'
  19. });
  20. CKEDITOR.dialog.add('youtube', function (instance) {
  21. var video,
  22. disabled = editor.config.youtube_disabled_fields || [];
  23. return {
  24. title : editor.lang.youtube.title,
  25. minWidth : 510,
  26. minHeight : 200,
  27. onShow: function () {
  28. for (var i = 0; i < disabled.length; i++) {
  29. this.getContentElement('youtubePlugin', disabled[i]).disable();
  30. }
  31. },
  32. contents :
  33. [{
  34. id : 'youtubePlugin',
  35. expand : true,
  36. elements :
  37. [{
  38. id : 'txtEmbed',
  39. type : 'textarea',
  40. label : editor.lang.youtube.txtEmbed,
  41. onChange : function (api) {
  42. handleEmbedChange(this, api);
  43. },
  44. onKeyUp : function (api) {
  45. handleEmbedChange(this, api);
  46. },
  47. validate : function () {
  48. if (this.isEnabled()) {
  49. if (!this.getValue()) {
  50. alert(editor.lang.youtube.noCode);
  51. return false;
  52. }
  53. else
  54. if (this.getValue().length === 0 || this.getValue().indexOf('//') === -1) {
  55. alert(editor.lang.youtube.invalidEmbed);
  56. return false;
  57. }
  58. }
  59. }
  60. },
  61. {
  62. type : 'html',
  63. html : editor.lang.youtube.or + '<hr>'
  64. },
  65. {
  66. type : 'hbox',
  67. widths : [ '70%', '15%', '15%' ],
  68. children :
  69. [
  70. {
  71. id : 'txtUrl',
  72. type : 'text',
  73. label : editor.lang.youtube.txtUrl,
  74. onChange : function (api) {
  75. handleLinkChange(this, api);
  76. },
  77. onKeyUp : function (api) {
  78. handleLinkChange(this, api);
  79. },
  80. validate : function () {
  81. if (this.isEnabled()) {
  82. if (!this.getValue()) {
  83. alert(editor.lang.youtube.noCode);
  84. return false;
  85. }
  86. else{
  87. video = ytVidId(this.getValue());
  88. if (this.getValue().length === 0 || video === false)
  89. {
  90. alert(editor.lang.youtube.invalidUrl);
  91. return false;
  92. }
  93. }
  94. }
  95. }
  96. },
  97. {
  98. type : 'text',
  99. id : 'txtWidth',
  100. width : '60px',
  101. label : editor.lang.youtube.txtWidth,
  102. 'default' : editor.config.youtube_width != null ? editor.config.youtube_width : '640',
  103. validate : function () {
  104. if (this.getValue()) {
  105. var width = parseInt (this.getValue()) || 0;
  106. if (width === 0) {
  107. alert(editor.lang.youtube.invalidWidth);
  108. return false;
  109. }
  110. }
  111. else {
  112. alert(editor.lang.youtube.noWidth);
  113. return false;
  114. }
  115. }
  116. },
  117. {
  118. type : 'text',
  119. id : 'txtHeight',
  120. width : '60px',
  121. label : editor.lang.youtube.txtHeight,
  122. 'default' : editor.config.youtube_height != null ? editor.config.youtube_height : '360',
  123. validate : function () {
  124. if (this.getValue()) {
  125. var height = parseInt(this.getValue()) || 0;
  126. if (height === 0) {
  127. alert(editor.lang.youtube.invalidHeight);
  128. return false;
  129. }
  130. }
  131. else {
  132. alert(editor.lang.youtube.noHeight);
  133. return false;
  134. }
  135. }
  136. }
  137. ]
  138. },
  139. {
  140. type : 'hbox',
  141. widths : [ '55%', '45%' ],
  142. children :
  143. [
  144. {
  145. id : 'chkResponsive',
  146. type : 'checkbox',
  147. label : editor.lang.youtube.txtResponsive,
  148. 'default' : editor.config.youtube_responsive != null ? editor.config.youtube_responsive : false
  149. },
  150. {
  151. id : 'chkNoEmbed',
  152. type : 'checkbox',
  153. label : editor.lang.youtube.txtNoEmbed,
  154. 'default' : editor.config.youtube_noembed != null ? editor.config.youtube_noembed : false
  155. }
  156. ]
  157. },
  158. {
  159. type : 'hbox',
  160. widths : [ '55%', '45%' ],
  161. children :
  162. [
  163. {
  164. id : 'chkRelated',
  165. type : 'checkbox',
  166. 'default' : editor.config.youtube_related != null ? editor.config.youtube_related : true,
  167. label : editor.lang.youtube.chkRelated
  168. },
  169. {
  170. id : 'chkOlderCode',
  171. type : 'checkbox',
  172. 'default' : editor.config.youtube_older != null ? editor.config.youtube_older : false,
  173. label : editor.lang.youtube.chkOlderCode
  174. }
  175. ]
  176. },
  177. {
  178. type : 'hbox',
  179. widths : [ '55%', '45%' ],
  180. children :
  181. [
  182. {
  183. id : 'chkPrivacy',
  184. type : 'checkbox',
  185. label : editor.lang.youtube.chkPrivacy,
  186. 'default' : editor.config.youtube_privacy != null ? editor.config.youtube_privacy : false
  187. },
  188. {
  189. id : 'chkAutoplay',
  190. type : 'checkbox',
  191. 'default' : editor.config.youtube_autoplay != null ? editor.config.youtube_autoplay : false,
  192. label : editor.lang.youtube.chkAutoplay
  193. }
  194. ]
  195. },
  196. {
  197. type : 'hbox',
  198. widths : [ '55%', '45%'],
  199. children :
  200. [
  201. {
  202. id : 'txtStartAt',
  203. type : 'text',
  204. label : editor.lang.youtube.txtStartAt,
  205. validate : function () {
  206. if (this.getValue()) {
  207. var str = this.getValue();
  208. if (!/^(?:(?:([01]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d)$/i.test(str)) {
  209. alert(editor.lang.youtube.invalidTime);
  210. return false;
  211. }
  212. }
  213. }
  214. },
  215. {
  216. id : 'chkControls',
  217. type : 'checkbox',
  218. 'default' : editor.config.youtube_controls != null ? editor.config.youtube_controls : true,
  219. label : editor.lang.youtube.chkControls
  220. }
  221. ]
  222. }
  223. ]
  224. }
  225. ],
  226. onOk: function()
  227. {
  228. var content = '';
  229. var responsiveStyle = '';
  230. if (this.getContentElement('youtubePlugin', 'txtEmbed').isEnabled()) {
  231. content = this.getValueOf('youtubePlugin', 'txtEmbed');
  232. }
  233. else {
  234. var url = 'https://', params = [], startSecs;
  235. var width = this.getValueOf('youtubePlugin', 'txtWidth');
  236. var height = this.getValueOf('youtubePlugin', 'txtHeight');
  237. if (this.getContentElement('youtubePlugin', 'chkPrivacy').getValue() === true) {
  238. url += 'www.youtube-nocookie.com/';
  239. }
  240. else {
  241. url += 'www.youtube.com/';
  242. }
  243. url += 'embed/' + video;
  244. if (this.getContentElement('youtubePlugin', 'chkRelated').getValue() === false) {
  245. params.push('rel=0');
  246. }
  247. if (this.getContentElement('youtubePlugin', 'chkAutoplay').getValue() === true) {
  248. params.push('autoplay=1');
  249. }
  250. if (this.getContentElement('youtubePlugin', 'chkControls').getValue() === false) {
  251. params.push('controls=0');
  252. }
  253. startSecs = this.getValueOf('youtubePlugin', 'txtStartAt');
  254. if (startSecs) {
  255. var seconds = hmsToSeconds(startSecs);
  256. params.push('start=' + seconds);
  257. }
  258. if (params.length > 0) {
  259. url = url + '?' + params.join('&');
  260. }
  261. if (this.getContentElement('youtubePlugin', 'chkResponsive').getValue() === true) {
  262. content += '<div class="youtube-embed-wrapper" style="position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden">';
  263. responsiveStyle = 'style="position:absolute;top:0;left:0;width:100%;height:100%"';
  264. }
  265. if (this.getContentElement('youtubePlugin', 'chkOlderCode').getValue() === true) {
  266. url = url.replace('embed/', 'v/');
  267. url = url.replace(/&/g, '&amp;');
  268. if (url.indexOf('?') === -1) {
  269. url += '?';
  270. }
  271. else {
  272. url += '&amp;';
  273. }
  274. url += 'hl=' + (this.getParentEditor().config.language ? this.getParentEditor().config.language : 'en') + '&amp;version=3';
  275. content += '<object width="' + width + '" height="' + height + '" ' + responsiveStyle + '>';
  276. content += '<param name="movie" value="' + url + '"></param>';
  277. content += '<param name="allowFullScreen" value="true"></param>';
  278. content += '<param name="allowscriptaccess" value="always"></param>';
  279. content += '<embed src="' + url + '" type="application/x-shockwave-flash" ';
  280. content += 'width="' + width + '" height="' + height + '" '+ responsiveStyle + ' allowscriptaccess="always" ';
  281. content += 'allowfullscreen="true"></embed>';
  282. content += '</object>';
  283. }
  284. else
  285. if (this.getContentElement('youtubePlugin', 'chkNoEmbed').getValue() === true) {
  286. var imgSrc = '//img.youtube.com/vi/' + video + '/sddefault.jpg';
  287. content += '<a href="' + url + '" ><img width="' + width + '" height="' + height + '" src="' + imgSrc + '" ' + responsiveStyle + '/></a>';
  288. }
  289. else {
  290. content += '<iframe width="' + width + '" height="' + height + '" src="' + url + '" ' + responsiveStyle;
  291. content += 'frameborder="0" allowfullscreen></iframe>';
  292. }
  293. if (this.getContentElement('youtubePlugin', 'chkResponsive').getValue() === true) {
  294. content += '</div>';
  295. }
  296. }
  297. var element = CKEDITOR.dom.element.createFromHtml(content);
  298. var instance = this.getParentEditor();
  299. instance.insertElement(element);
  300. }
  301. };
  302. });
  303. }
  304. });
  305. })();
  306. function handleLinkChange(el, api) {
  307. var video = ytVidId(el.getValue());
  308. var time = ytVidTime(el.getValue());
  309. if (el.getValue().length > 0) {
  310. el.getDialog().getContentElement('youtubePlugin', 'txtEmbed').disable();
  311. }
  312. else {
  313. el.getDialog().getContentElement('youtubePlugin', 'txtEmbed').enable();
  314. }
  315. if (video && time) {
  316. var seconds = timeParamToSeconds(time);
  317. var hms = secondsToHms(seconds);
  318. el.getDialog().getContentElement('youtubePlugin', 'txtStartAt').setValue(hms);
  319. }
  320. }
  321. function handleEmbedChange(el, api) {
  322. if (el.getValue().length > 0) {
  323. el.getDialog().getContentElement('youtubePlugin', 'txtUrl').disable();
  324. }
  325. else {
  326. el.getDialog().getContentElement('youtubePlugin', 'txtUrl').enable();
  327. }
  328. }
  329. /**
  330. * JavaScript function to match (and return) the video Id
  331. * of any valid Youtube Url, given as input string.
  332. * @author: Stephan Schmitz <eyecatchup@gmail.com>
  333. * @url: http://stackoverflow.com/a/10315969/624466
  334. */
  335. function ytVidId(url) {
  336. var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
  337. return (url.match(p)) ? RegExp.$1 : false;
  338. }
  339. /**
  340. * Matches and returns time param in YouTube Urls.
  341. */
  342. function ytVidTime(url) {
  343. var p = /t=([0-9hms]+)/;
  344. return (url.match(p)) ? RegExp.$1 : false;
  345. }
  346. /**
  347. * Converts time in hms format to seconds only
  348. */
  349. function hmsToSeconds(time) {
  350. var arr = time.split(':'), s = 0, m = 1;
  351. while (arr.length > 0) {
  352. s += m * parseInt(arr.pop(), 10);
  353. m *= 60;
  354. }
  355. return s;
  356. }
  357. /**
  358. * Converts seconds to hms format
  359. */
  360. function secondsToHms(seconds) {
  361. var h = Math.floor(seconds / 3600);
  362. var m = Math.floor((seconds / 60) % 60);
  363. var s = seconds % 60;
  364. var pad = function (n) {
  365. n = String(n);
  366. return n.length >= 2 ? n : "0" + n;
  367. };
  368. if (h > 0) {
  369. return pad(h) + ':' + pad(m) + ':' + pad(s);
  370. }
  371. else {
  372. return pad(m) + ':' + pad(s);
  373. }
  374. }
  375. /**
  376. * Converts time in youtube t-param format to seconds
  377. */
  378. function timeParamToSeconds(param) {
  379. var componentValue = function (si) {
  380. var regex = new RegExp('(\\d+)' + si);
  381. return param.match(regex) ? parseInt(RegExp.$1, 10) : 0;
  382. };
  383. return componentValue('h') * 3600
  384. + componentValue('m') * 60
  385. + componentValue('s');
  386. }
  387. /**
  388. * Converts seconds into youtube t-param value, e.g. 1h4m30s
  389. */
  390. function secondsToTimeParam(seconds) {
  391. var h = Math.floor(seconds / 3600);
  392. var m = Math.floor((seconds / 60) % 60);
  393. var s = seconds % 60;
  394. var param = '';
  395. if (h > 0) {
  396. param += h + 'h';
  397. }
  398. if (m > 0) {
  399. param += m + 'm';
  400. }
  401. if (s > 0) {
  402. param += s + 's';
  403. }
  404. return param;
  405. }