(function (factory) { /* global define */ if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else { // Browser globals: jQuery factory(window.jQuery); } }(function ($) { // template var tmpl = $.summernote.renderer.getTemplate(); // core functions: range, dom var range = $.summernote.core.range; var dom = $.summernote.core.dom; /** * createVideoNode * * @member plugin.video * @private * @param {String} url * @return {Node} */ var createVideoNode = function (url) { // video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm) var ytRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/; var ytMatch = url.match(ytRegExp); var igRegExp = /\/\/instagram.com\/p\/(.[a-zA-Z0-9]*)/; var igMatch = url.match(igRegExp); var vRegExp = /\/\/vine.co\/v\/(.[a-zA-Z0-9]*)/; var vMatch = url.match(vRegExp); var vimRegExp = /\/\/(player.)?vimeo.com\/([a-z]*\/)*([0-9]{6,11})[?]?.*/; var vimMatch = url.match(vimRegExp); var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/; var dmMatch = url.match(dmRegExp); var youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/; var youkuMatch = url.match(youkuRegExp); var mp4RegExp = /^.+.(mp4|m4v)$/; var mp4Match = url.match(mp4RegExp); var oggRegExp = /^.+.(ogg|ogv)$/; var oggMatch = url.match(oggRegExp); var webmRegExp = /^.+.(webm)$/; var webmMatch = url.match(webmRegExp); var $video; if (ytMatch && ytMatch[1].length === 11) { var youtubeId = ytMatch[1]; $video = $('<iframe>') .attr('frameborder', 0) .attr('src', '//www.youtube.com/embed/' + youtubeId) .attr('width', '640').attr('height', '360'); } else if (igMatch && igMatch[0].length) { $video = $('<iframe>') .attr('frameborder', 0) .attr('src', igMatch[0] + '/embed/') .attr('width', '612').attr('height', '710') .attr('scrolling', 'no') .attr('allowtransparency', 'true'); } else if (vMatch && vMatch[0].length) { $video = $('<iframe>') .attr('frameborder', 0) .attr('src', vMatch[0] + '/embed/simple') .attr('width', '600').attr('height', '600') .attr('class', 'vine-embed'); } else if (vimMatch && vimMatch[3].length) { $video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>') .attr('frameborder', 0) .attr('src', '//player.vimeo.com/video/' + vimMatch[3]) .attr('width', '640').attr('height', '360'); } else if (dmMatch && dmMatch[2].length) { $video = $('<iframe>') .attr('frameborder', 0) .attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2]) .attr('width', '640').attr('height', '360'); } else if (youkuMatch && youkuMatch[1].length) { $video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>') .attr('frameborder', 0) .attr('height', '498') .attr('width', '510') .attr('src', '//player.youku.com/embed/' + youkuMatch[1]); } else if (mp4Match || oggMatch || webmMatch) { $video = $('<video controls>') .attr('src', url) .attr('width', '640').attr('height', '360'); } else { // this is not a known video link. Now what, Cat? Now what? return false; } return $video[0]; }; /** * @member plugin.video * @private * @param {jQuery} $editable * @return {String} */ var getTextOnRange = function ($editable) { $editable.focus(); var rng = range.create(); // if range on anchor, expand range with anchor if (rng.isOnAnchor()) { var anchor = dom.ancestor(rng.sc, dom.isAnchor); rng = range.createFromNode(anchor); } return rng.toString(); }; /** * toggle button status * * @member plugin.video * @private * @param {jQuery} $btn * @param {Boolean} isEnable */ var toggleBtn = function ($btn, isEnable) { $btn.toggleClass('disabled', !isEnable); $btn.attr('disabled', !isEnable); }; /** * Show video dialog and set event handlers on dialog controls. * * @member plugin.video * @private * @param {jQuery} $dialog * @param {jQuery} $dialog * @param {Object} text * @return {Promise} */ var showVideoDialog = function ($editable, $dialog, text) { return $.Deferred(function (deferred) { var $videoDialog = $dialog.find('.note-video-dialog'); var $videoUrl = $videoDialog.find('.note-video-url'), $videoBtn = $videoDialog.find('.note-video-btn'); $videoDialog.one('shown.bs.modal', function () { $videoUrl.val(text).on('input', function () { toggleBtn($videoBtn, $videoUrl.val()); }).trigger('focus'); $videoBtn.click(function (event) { event.preventDefault(); deferred.resolve($videoUrl.val()); $videoDialog.modal('hide'); }); }).one('hidden.bs.modal', function () { $videoUrl.off('input'); $videoBtn.off('click'); if (deferred.state() === 'pending') { deferred.reject(); } }).modal('show'); }); }; /** * @class plugin.video * * Video Plugin * * video plugin is to make embeded video tag. * * ### load script * * ``` * < script src="plugin/summernote-ext-video.js"></script > * ``` * * ### use a plugin in toolbar * ``` * $("#editor").summernote({ * ... * toolbar : [ * ['group', [ 'video' ]] * ] * ... * }); * ``` */ $.summernote.addPlugin({ /** @property {String} name name of plugin */ name: 'video', /** * @property {Object} buttons * @property {function(object): string} buttons.video */ buttons: { video: function (lang, options) { return tmpl.iconButton(options.iconPrefix + 'youtube-play', { event: 'showVideoDialog', title: lang.video.video, hide: true }); } }, /** * @property {Object} dialogs * @property {function(object, object): string} dialogs.video */ dialogs: { video: function (lang) { var body = '<div class="form-group row-fluid">' + '<label>' + lang.video.url + ' <small class="text-muted">' + lang.video.providers + '</small></label>' + '<input class="note-video-url form-control span12" type="text" />' + '</div>'; var footer = '<button href="#" class="btn btn-primary note-video-btn disabled" disabled>' + lang.video.insert + '</button>'; return tmpl.dialog('note-video-dialog', lang.video.insert, body, footer); } }, /** * @property {Object} events * @property {Function} events.showVideoDialog */ events: { showVideoDialog: function (event, editor, layoutInfo) { var $dialog = layoutInfo.dialog(), $editable = layoutInfo.editable(), text = getTextOnRange($editable); // save current range editor.saveRange($editable); showVideoDialog($editable, $dialog, text).then(function (url) { // when ok button clicked // restore range editor.restoreRange($editable); // build node var $node = createVideoNode(url); if ($node) { // insert video node editor.insertNode($editable, $node); } }).fail(function () { // when cancel button clicked editor.restoreRange($editable); }); } }, // define language langs: { 'en-US': { video: { video: 'Video', videoLink: 'Video Link', insert: 'Insert Video', url: 'Video URL?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)' } }, 'ar-AR': { video: { video: 'فيديو', videoLink: 'رابط الفيديو', insert: 'إدراج الفيديو', url: 'رابط الفيديو', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ou Youku)' } }, 'ca-ES': { video: { video: 'Video', videoLink: 'Enllaç del video', insert: 'Inserir video', url: 'URL del video?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, o Youku)' } }, 'cs-CZ': { video: { video: 'Video', videoLink: 'Odkaz videa', insert: 'Vložit video', url: 'URL videa?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion nebo Youku)' } }, 'da-DK': { video: { video: 'Video', videoLink: 'Video Link', insert: 'Indsæt Video', url: 'Video URL?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)' } }, 'de-DE': { video: { video: 'Video', videoLink: 'Video Link', insert: 'Video einfügen', url: 'Video URL?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, oder Youku)' } }, 'es-ES': { video: { video: 'Video', videoLink: 'Link del video', insert: 'Insertar video', url: '¿URL del video?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, o Youku)' } }, 'es-EU': { video: { video: 'Bideoa', videoLink: 'Bideorako esteka', insert: 'Bideo berri bat txertatu', url: 'Bideoaren URL helbidea', providers: '(YouTube, Vimeo, Vine, Instagram, edo DailyMotion)' } }, 'fa-IR': { video: { video: 'ویدیو', videoLink: 'لینک ویدیو', insert: 'افزودن ویدیو', url: 'آدرس ویدیو ؟', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, یا Youku)' } }, 'fi-FI': { video: { video: 'Video', videoLink: 'Linkki videoon', insert: 'Lisää video', url: 'Videon URL-osoite?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion tai Youku)' } }, 'fr-FR': { video: { video: 'Vidéo', videoLink: 'Lien vidéo', insert: 'Insérer une vidéo', url: 'URL de la vidéo', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ou Youku)' } }, 'he-IL': { video: { video: 'סרטון', videoLink: 'קישור לסרטון', insert: 'הוסף סרטון', url: 'קישור לסרטון', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion או Youku)' } }, 'hu-HU': { video: { video: 'Videó', videoLink: 'Videó hivatkozás', insert: 'Videó beszúrása', url: 'Videó URL címe', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, vagy Youku)' } }, 'id-ID': { video: { video: 'Video', videoLink: 'Link video', insert: 'Sisipkan video', url: 'Tautan video', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, atau Youku)' } }, 'it-IT': { video: { video: 'Video', videoLink: 'Collegamento ad un Video', insert: 'Inserisci Video', url: 'URL del Video', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion o Youku)' } }, 'ja-JP': { video: { video: '動画', videoLink: '動画リンク', insert: '動画挿入', url: '動画のURL', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, Youku)' } }, 'ko-KR': { video: { video: '동영상', videoLink: '동영상 링크', insert: '동영상 추가', url: '동영상 URL', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, Youku 사용 가능)' } }, 'nb-NO': { video: { video: 'Video', videoLink: 'Videolenke', insert: 'Sett inn video', url: 'Video-URL', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)' } }, 'nl-NL': { video: { video: 'Video', videoLink: 'Video link', insert: 'Video invoegen', url: 'URL van de video', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion of Youku)' } }, 'pl-PL': { video: { video: 'Wideo', videoLink: 'Adres wideo', insert: 'Wstaw wideo', url: 'Adres wideo', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, lub Youku)' } }, 'pt-BR': { video: { video: 'Vídeo', videoLink: 'Link para vídeo', insert: 'Inserir vídeo', url: 'URL do vídeo?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, ou Youku)' } }, 'ro-RO': { video: { video: 'Video', videoLink: 'Link video', insert: 'Inserează video', url: 'URL video?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion, sau Youku)' } }, 'ru-RU': { video: { video: 'Видео', videoLink: 'Ссылка на видео', insert: 'Вставить видео', url: 'URL видео', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion или Youku)' } }, 'sk-SK': { video: { video: 'Video', videoLink: 'Odkaz videa', insert: 'Vložiť video', url: 'URL videa?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion nebo Youku)' } }, 'sl-SI': { video: { video: 'Video', videoLink: 'Video povezava', insert: 'Vstavi video', url: 'Povezava do videa', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ali Youku)' } }, 'sr-RS': { video: { video: 'Видео', videoLink: 'Веза ка видеу', insert: 'Уметни видео', url: 'URL видео', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion или Youku)' } }, 'sr-RS-Latin': { video: { video: 'Video', videoLink: 'Veza ka videu', insert: 'Umetni video', url: 'URL video', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion ili Youku)' } }, 'sv-SE': { video: { video: 'Filmklipp', videoLink: 'Länk till filmklipp', insert: 'Infoga filmklipp', url: 'Länk till filmklipp', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion eller Youku)' } }, 'th-TH': { video: { video: 'วีดีโอ', videoLink: 'ลิงก์ของวีดีโอ', insert: 'แทรกวีดีโอ', url: 'ที่อยู่ URL ของวีดีโอ?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion หรือ Youku)' } }, 'tr-TR': { video: { video: 'Video', videoLink: 'Video bağlantısı', insert: 'Video ekle', url: 'Video bağlantısı?', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion veya Youku)' } }, 'uk-UA': { video: { video: 'Відео', videoLink: 'Посилання на відео', insert: 'Вставити відео', url: 'URL відео', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion чи Youku)' } }, 'vi-VN': { video: { video: 'Video', videoLink: 'Đường Dẫn đến Video', insert: 'Chèn Video', url: 'URL', providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion và Youku)' } }, 'zh-CN': { video: { video: '视频', videoLink: '视频链接', insert: '插入视频', url: '视频地址', providers: '(优酷, Instagram, DailyMotion, Youtube等)' } }, 'zh-TW': { video: { video: '影片', videoLink: '影片連結', insert: '插入影片', url: '影片網址', providers: '(優酷, Instagram, DailyMotion, Youtube等)' } } } }); }));