summernote-ext-video.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. (function (factory) {
  2. /* global define */
  3. if (typeof define === 'function' && define.amd) {
  4. // AMD. Register as an anonymous module.
  5. define(['jquery'], factory);
  6. } else {
  7. // Browser globals: jQuery
  8. factory(window.jQuery);
  9. }
  10. }(function ($) {
  11. // template
  12. var tmpl = $.summernote.renderer.getTemplate();
  13. // core functions: range, dom
  14. var range = $.summernote.core.range;
  15. var dom = $.summernote.core.dom;
  16. /**
  17. * createVideoNode
  18. *
  19. * @member plugin.video
  20. * @private
  21. * @param {String} url
  22. * @return {Node}
  23. */
  24. var createVideoNode = function (url) {
  25. // video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm)
  26. var ytRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
  27. var ytMatch = url.match(ytRegExp);
  28. var igRegExp = /\/\/instagram.com\/p\/(.[a-zA-Z0-9]*)/;
  29. var igMatch = url.match(igRegExp);
  30. var vRegExp = /\/\/vine.co\/v\/(.[a-zA-Z0-9]*)/;
  31. var vMatch = url.match(vRegExp);
  32. var vimRegExp = /\/\/(player.)?vimeo.com\/([a-z]*\/)*([0-9]{6,11})[?]?.*/;
  33. var vimMatch = url.match(vimRegExp);
  34. var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
  35. var dmMatch = url.match(dmRegExp);
  36. var youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/;
  37. var youkuMatch = url.match(youkuRegExp);
  38. var mp4RegExp = /^.+.(mp4|m4v)$/;
  39. var mp4Match = url.match(mp4RegExp);
  40. var oggRegExp = /^.+.(ogg|ogv)$/;
  41. var oggMatch = url.match(oggRegExp);
  42. var webmRegExp = /^.+.(webm)$/;
  43. var webmMatch = url.match(webmRegExp);
  44. var $video;
  45. if (ytMatch && ytMatch[1].length === 11) {
  46. var youtubeId = ytMatch[1];
  47. $video = $('<iframe>')
  48. .attr('frameborder', 0)
  49. .attr('src', '//www.youtube.com/embed/' + youtubeId)
  50. .attr('width', '640').attr('height', '360');
  51. } else if (igMatch && igMatch[0].length) {
  52. $video = $('<iframe>')
  53. .attr('frameborder', 0)
  54. .attr('src', igMatch[0] + '/embed/')
  55. .attr('width', '612').attr('height', '710')
  56. .attr('scrolling', 'no')
  57. .attr('allowtransparency', 'true');
  58. } else if (vMatch && vMatch[0].length) {
  59. $video = $('<iframe>')
  60. .attr('frameborder', 0)
  61. .attr('src', vMatch[0] + '/embed/simple')
  62. .attr('width', '600').attr('height', '600')
  63. .attr('class', 'vine-embed');
  64. } else if (vimMatch && vimMatch[3].length) {
  65. $video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
  66. .attr('frameborder', 0)
  67. .attr('src', '//player.vimeo.com/video/' + vimMatch[3])
  68. .attr('width', '640').attr('height', '360');
  69. } else if (dmMatch && dmMatch[2].length) {
  70. $video = $('<iframe>')
  71. .attr('frameborder', 0)
  72. .attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
  73. .attr('width', '640').attr('height', '360');
  74. } else if (youkuMatch && youkuMatch[1].length) {
  75. $video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
  76. .attr('frameborder', 0)
  77. .attr('height', '498')
  78. .attr('width', '510')
  79. .attr('src', '//player.youku.com/embed/' + youkuMatch[1]);
  80. } else if (mp4Match || oggMatch || webmMatch) {
  81. $video = $('<video controls>')
  82. .attr('src', url)
  83. .attr('width', '640').attr('height', '360');
  84. } else {
  85. // this is not a known video link. Now what, Cat? Now what?
  86. return false;
  87. }
  88. return $video[0];
  89. };
  90. /**
  91. * @member plugin.video
  92. * @private
  93. * @param {jQuery} $editable
  94. * @return {String}
  95. */
  96. var getTextOnRange = function ($editable) {
  97. $editable.focus();
  98. var rng = range.create();
  99. // if range on anchor, expand range with anchor
  100. if (rng.isOnAnchor()) {
  101. var anchor = dom.ancestor(rng.sc, dom.isAnchor);
  102. rng = range.createFromNode(anchor);
  103. }
  104. return rng.toString();
  105. };
  106. /**
  107. * toggle button status
  108. *
  109. * @member plugin.video
  110. * @private
  111. * @param {jQuery} $btn
  112. * @param {Boolean} isEnable
  113. */
  114. var toggleBtn = function ($btn, isEnable) {
  115. $btn.toggleClass('disabled', !isEnable);
  116. $btn.attr('disabled', !isEnable);
  117. };
  118. /**
  119. * Show video dialog and set event handlers on dialog controls.
  120. *
  121. * @member plugin.video
  122. * @private
  123. * @param {jQuery} $dialog
  124. * @param {jQuery} $dialog
  125. * @param {Object} text
  126. * @return {Promise}
  127. */
  128. var showVideoDialog = function ($editable, $dialog, text) {
  129. return $.Deferred(function (deferred) {
  130. var $videoDialog = $dialog.find('.note-video-dialog');
  131. var $videoUrl = $videoDialog.find('.note-video-url'),
  132. $videoBtn = $videoDialog.find('.note-video-btn');
  133. $videoDialog.one('shown.bs.modal', function () {
  134. $videoUrl.val(text).on('input', function () {
  135. toggleBtn($videoBtn, $videoUrl.val());
  136. }).trigger('focus');
  137. $videoBtn.click(function (event) {
  138. event.preventDefault();
  139. deferred.resolve($videoUrl.val());
  140. $videoDialog.modal('hide');
  141. });
  142. }).one('hidden.bs.modal', function () {
  143. $videoUrl.off('input');
  144. $videoBtn.off('click');
  145. if (deferred.state() === 'pending') {
  146. deferred.reject();
  147. }
  148. }).modal('show');
  149. });
  150. };
  151. /**
  152. * @class plugin.video
  153. *
  154. * Video Plugin
  155. *
  156. * video plugin is to make embeded video tag.
  157. *
  158. * ### load script
  159. *
  160. * ```
  161. * < script src="plugin/summernote-ext-video.js"></script >
  162. * ```
  163. *
  164. * ### use a plugin in toolbar
  165. * ```
  166. * $("#editor").summernote({
  167. * ...
  168. * toolbar : [
  169. * ['group', [ 'video' ]]
  170. * ]
  171. * ...
  172. * });
  173. * ```
  174. */
  175. $.summernote.addPlugin({
  176. /** @property {String} name name of plugin */
  177. name: 'video',
  178. /**
  179. * @property {Object} buttons
  180. * @property {function(object): string} buttons.video
  181. */
  182. buttons: {
  183. video: function (lang, options) {
  184. return tmpl.iconButton(options.iconPrefix + 'youtube-play', {
  185. event: 'showVideoDialog',
  186. title: lang.video.video,
  187. hide: true
  188. });
  189. }
  190. },
  191. /**
  192. * @property {Object} dialogs
  193. * @property {function(object, object): string} dialogs.video
  194. */
  195. dialogs: {
  196. video: function (lang) {
  197. var body = '<div class="form-group row-fluid">' +
  198. '<label>' + lang.video.url + ' <small class="text-muted">' + lang.video.providers + '</small></label>' +
  199. '<input class="note-video-url form-control span12" type="text" />' +
  200. '</div>';
  201. var footer = '<button href="#" class="btn btn-primary note-video-btn disabled" disabled>' + lang.video.insert + '</button>';
  202. return tmpl.dialog('note-video-dialog', lang.video.insert, body, footer);
  203. }
  204. },
  205. /**
  206. * @property {Object} events
  207. * @property {Function} events.showVideoDialog
  208. */
  209. events: {
  210. showVideoDialog: function (event, editor, layoutInfo) {
  211. var $dialog = layoutInfo.dialog(),
  212. $editable = layoutInfo.editable(),
  213. text = getTextOnRange($editable);
  214. // save current range
  215. editor.saveRange($editable);
  216. showVideoDialog($editable, $dialog, text).then(function (url) {
  217. // when ok button clicked
  218. // restore range
  219. editor.restoreRange($editable);
  220. // build node
  221. var $node = createVideoNode(url);
  222. if ($node) {
  223. // insert video node
  224. editor.insertNode($editable, $node);
  225. }
  226. }).fail(function () {
  227. // when cancel button clicked
  228. editor.restoreRange($editable);
  229. });
  230. }
  231. },
  232. // define language
  233. langs: {
  234. 'en-US': {
  235. video: {
  236. video: 'Video',
  237. videoLink: 'Video Link',
  238. insert: 'Insert Video',
  239. url: 'Video URL?',
  240. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)'
  241. }
  242. },
  243. 'ar-AR': {
  244. video: {
  245. video: 'فيديو',
  246. videoLink: 'رابط الفيديو',
  247. insert: 'إدراج الفيديو',
  248. url: 'رابط الفيديو',
  249. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ou Youku)'
  250. }
  251. },
  252. 'ca-ES': {
  253. video: {
  254. video: 'Video',
  255. videoLink: 'Enllaç del video',
  256. insert: 'Inserir video',
  257. url: 'URL del video?',
  258. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, o Youku)'
  259. }
  260. },
  261. 'cs-CZ': {
  262. video: {
  263. video: 'Video',
  264. videoLink: 'Odkaz videa',
  265. insert: 'Vložit video',
  266. url: 'URL videa?',
  267. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion nebo Youku)'
  268. }
  269. },
  270. 'da-DK': {
  271. video: {
  272. video: 'Video',
  273. videoLink: 'Video Link',
  274. insert: 'Indsæt Video',
  275. url: 'Video URL?',
  276. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)'
  277. }
  278. },
  279. 'de-DE': {
  280. video: {
  281. video: 'Video',
  282. videoLink: 'Video Link',
  283. insert: 'Video einfügen',
  284. url: 'Video URL?',
  285. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, oder Youku)'
  286. }
  287. },
  288. 'es-ES': {
  289. video: {
  290. video: 'Video',
  291. videoLink: 'Link del video',
  292. insert: 'Insertar video',
  293. url: '¿URL del video?',
  294. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, o Youku)'
  295. }
  296. },
  297. 'es-EU': {
  298. video: {
  299. video: 'Bideoa',
  300. videoLink: 'Bideorako esteka',
  301. insert: 'Bideo berri bat txertatu',
  302. url: 'Bideoaren URL helbidea',
  303. providers: '(YouTube, Vimeo, Vine, Instagram, edo DailyMotion)'
  304. }
  305. },
  306. 'fa-IR': {
  307. video: {
  308. video: 'ویدیو',
  309. videoLink: 'لینک ویدیو',
  310. insert: 'افزودن ویدیو',
  311. url: 'آدرس ویدیو ؟',
  312. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, یا Youku)'
  313. }
  314. },
  315. 'fi-FI': {
  316. video: {
  317. video: 'Video',
  318. videoLink: 'Linkki videoon',
  319. insert: 'Lisää video',
  320. url: 'Videon URL-osoite?',
  321. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion tai Youku)'
  322. }
  323. },
  324. 'fr-FR': {
  325. video: {
  326. video: 'Vidéo',
  327. videoLink: 'Lien vidéo',
  328. insert: 'Insérer une vidéo',
  329. url: 'URL de la vidéo',
  330. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ou Youku)'
  331. }
  332. },
  333. 'he-IL': {
  334. video: {
  335. video: 'סרטון',
  336. videoLink: 'קישור לסרטון',
  337. insert: 'הוסף סרטון',
  338. url: 'קישור לסרטון',
  339. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion או Youku)'
  340. }
  341. },
  342. 'hu-HU': {
  343. video: {
  344. video: 'Videó',
  345. videoLink: 'Videó hivatkozás',
  346. insert: 'Videó beszúrása',
  347. url: 'Videó URL címe',
  348. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, vagy Youku)'
  349. }
  350. },
  351. 'id-ID': {
  352. video: {
  353. video: 'Video',
  354. videoLink: 'Link video',
  355. insert: 'Sisipkan video',
  356. url: 'Tautan video',
  357. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, atau Youku)'
  358. }
  359. },
  360. 'it-IT': {
  361. video: {
  362. video: 'Video',
  363. videoLink: 'Collegamento ad un Video',
  364. insert: 'Inserisci Video',
  365. url: 'URL del Video',
  366. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion o Youku)'
  367. }
  368. },
  369. 'ja-JP': {
  370. video: {
  371. video: '動画',
  372. videoLink: '動画リンク',
  373. insert: '動画挿入',
  374. url: '動画のURL',
  375. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, Youku)'
  376. }
  377. },
  378. 'ko-KR': {
  379. video: {
  380. video: '동영상',
  381. videoLink: '동영상 링크',
  382. insert: '동영상 추가',
  383. url: '동영상 URL',
  384. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, Youku 사용 가능)'
  385. }
  386. },
  387. 'nb-NO': {
  388. video: {
  389. video: 'Video',
  390. videoLink: 'Videolenke',
  391. insert: 'Sett inn video',
  392. url: 'Video-URL',
  393. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)'
  394. }
  395. },
  396. 'nl-NL': {
  397. video: {
  398. video: 'Video',
  399. videoLink: 'Video link',
  400. insert: 'Video invoegen',
  401. url: 'URL van de video',
  402. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion of Youku)'
  403. }
  404. },
  405. 'pl-PL': {
  406. video: {
  407. video: 'Wideo',
  408. videoLink: 'Adres wideo',
  409. insert: 'Wstaw wideo',
  410. url: 'Adres wideo',
  411. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, lub Youku)'
  412. }
  413. },
  414. 'pt-BR': {
  415. video: {
  416. video: 'Vídeo',
  417. videoLink: 'Link para vídeo',
  418. insert: 'Inserir vídeo',
  419. url: 'URL do vídeo?',
  420. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, ou Youku)'
  421. }
  422. },
  423. 'ro-RO': {
  424. video: {
  425. video: 'Video',
  426. videoLink: 'Link video',
  427. insert: 'Inserează video',
  428. url: 'URL video?',
  429. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, sau Youku)'
  430. }
  431. },
  432. 'ru-RU': {
  433. video: {
  434. video: 'Видео',
  435. videoLink: 'Ссылка на видео',
  436. insert: 'Вставить видео',
  437. url: 'URL видео',
  438. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion или Youku)'
  439. }
  440. },
  441. 'sk-SK': {
  442. video: {
  443. video: 'Video',
  444. videoLink: 'Odkaz videa',
  445. insert: 'Vložiť video',
  446. url: 'URL videa?',
  447. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion nebo Youku)'
  448. }
  449. },
  450. 'sl-SI': {
  451. video: {
  452. video: 'Video',
  453. videoLink: 'Video povezava',
  454. insert: 'Vstavi video',
  455. url: 'Povezava do videa',
  456. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ali Youku)'
  457. }
  458. },
  459. 'sr-RS': {
  460. video: {
  461. video: 'Видео',
  462. videoLink: 'Веза ка видеу',
  463. insert: 'Уметни видео',
  464. url: 'URL видео',
  465. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion или Youku)'
  466. }
  467. },
  468. 'sr-RS-Latin': {
  469. video: {
  470. video: 'Video',
  471. videoLink: 'Veza ka videu',
  472. insert: 'Umetni video',
  473. url: 'URL video',
  474. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ili Youku)'
  475. }
  476. },
  477. 'sv-SE': {
  478. video: {
  479. video: 'Filmklipp',
  480. videoLink: 'Länk till filmklipp',
  481. insert: 'Infoga filmklipp',
  482. url: 'Länk till filmklipp',
  483. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)'
  484. }
  485. },
  486. 'th-TH': {
  487. video: {
  488. video: 'วีดีโอ',
  489. videoLink: 'ลิงก์ของวีดีโอ',
  490. insert: 'แทรกวีดีโอ',
  491. url: 'ที่อยู่ URL ของวีดีโอ?',
  492. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion หรือ Youku)'
  493. }
  494. },
  495. 'tr-TR': {
  496. video: {
  497. video: 'Video',
  498. videoLink: 'Video bağlantısı',
  499. insert: 'Video ekle',
  500. url: 'Video bağlantısı?',
  501. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion veya Youku)'
  502. }
  503. },
  504. 'uk-UA': {
  505. video: {
  506. video: 'Відео',
  507. videoLink: 'Посилання на відео',
  508. insert: 'Вставити відео',
  509. url: 'URL відео',
  510. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion чи Youku)'
  511. }
  512. },
  513. 'vi-VN': {
  514. video: {
  515. video: 'Video',
  516. videoLink: 'Đường Dẫn đến Video',
  517. insert: 'Chèn Video',
  518. url: 'URL',
  519. providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion và Youku)'
  520. }
  521. },
  522. 'zh-CN': {
  523. video: {
  524. video: '视频',
  525. videoLink: '视频链接',
  526. insert: '插入视频',
  527. url: '视频地址',
  528. providers: '(优酷, Instagram, DailyMotion, Youtube等)'
  529. }
  530. },
  531. 'zh-TW': {
  532. video: {
  533. video: '影片',
  534. videoLink: '影片連結',
  535. insert: '插入影片',
  536. url: '影片網址',
  537. providers: '(優酷, Instagram, DailyMotion, Youtube等)'
  538. }
  539. }
  540. }
  541. });
  542. }));