diff --git a/src/formats/bbcode.js b/src/formats/bbcode.js
index fc89cd0b7..341fc1a37 100644
--- a/src/formats/bbcode.js
+++ b/src/formats/bbcode.js
@@ -785,17 +785,38 @@
allowsEmpty: true,
tags: {
iframe: {
- 'data-youtube-id': null
+ 'data-youtube-id': null,
+ 'data-youtube-start': null
}
},
format: function (element, content) {
- element = attr(element, 'data-youtube-id');
+ var id = attr(element, 'data-youtube-id');
+ var start = attr(element, 'data-youtube-start');
- return element ? '[youtube]' + element + '[/youtube]' : content;
+ if (start > 0) {
+ return id ? '[youtube=' + start + ']' + id +
+ '[/youtube]' : content;
+ } else {
+ return id ? '[youtube]' + id + '[/youtube]' : content;
+ }
},
- html: ''
+ html: function (token, attrs, content) {
+ var pOpts = this.opts;
+ var id = content;
+ var start = 0;
+
+ if (attrs.defaultattr) {
+ start = escapeEntities(attrs.defaultattr);
+ }
+
+ return '';
+ }
},
// END_COMMAND
diff --git a/src/formats/xhtml.js b/src/formats/xhtml.js
index e59347fa0..252f95be0 100644
--- a/src/formats/xhtml.js
+++ b/src/formats/xhtml.js
@@ -180,17 +180,20 @@
youtube: {
txtExec: function (caller) {
var editor = this;
+ var pOpts = editor.opts.parserOptions;
getEditorCommand('youtube')._dropDown(
editor,
caller,
- function (id, time) {
+ function (id, start) {
editor.insertText(
- ''
+ 'data-youtube-start="' + start + '">' +
+ ''
);
}
);
diff --git a/src/icons/material.js b/src/icons/material.js
index 9c464eef5..b79f262de 100644
--- a/src/icons/material.js
+++ b/src/icons/material.js
@@ -74,7 +74,8 @@
'undo': '',
// Austin Andrews @Templarian - https://materialdesignicons.com/
'unlink': '',
- 'youtube': ''
+ 'youtube': '',
+ 'video': ''
};
sceditor.icons.material = function () {
diff --git a/src/icons/monocons.js b/src/icons/monocons.js
index 0e852cab8..4186eb75b 100644
--- a/src/icons/monocons.js
+++ b/src/icons/monocons.js
@@ -58,7 +58,8 @@
'underline': 'U',
'undo': '',
'unlink': '',
- 'youtube': ''
+ 'youtube': '',
+ 'video': ''
};
sceditor.icons.monocons = function () {
diff --git a/src/lib/SCEditor.js b/src/lib/SCEditor.js
index d2b67f312..53eab02ed 100644
--- a/src/lib/SCEditor.js
+++ b/src/lib/SCEditor.js
@@ -407,6 +407,15 @@ export default function SCEditor(original, userOptions) {
isRequired = original.required;
original.required = false;
+ // Plugins should be initiated before the formatters since plugin
+ // may add/change the handlers and format init should create cache
+ // with those changes
+ initPlugins();
+
+ // Plugins might change the commands, should re-build it
+ base.commands = utils
+ .extend(true, {}, (userOptions.commands || defaultCommands));
+
var FormatCtor = SCEditor.formats[options.format];
format = FormatCtor ? new FormatCtor() : {};
if ('init' in format) {
@@ -414,7 +423,6 @@ export default function SCEditor(original, userOptions) {
}
// create the editor
- initPlugins();
initEmoticons();
initToolBar();
initEditor();
diff --git a/src/lib/defaultCommands.js b/src/lib/defaultCommands.js
index a38932d6e..e6ed97861 100644
--- a/src/lib/defaultCommands.js
+++ b/src/lib/defaultCommands.js
@@ -777,11 +777,13 @@ var defaultCmds = {
},
exec: function (btn) {
var editor = this;
+ var pOpts = editor.opts.parserOptions;
defaultCmds.youtube._dropDown(editor, btn, function (id, time) {
editor.wysiwygEditorInsertHtml(_tmpl('youtube', {
id: id,
- time: time
+ time: time,
+ params: pOpts.youtubeParameters
}));
});
},
diff --git a/src/lib/defaultOptions.js b/src/lib/defaultOptions.js
index bff9a0237..c6afd6750 100644
--- a/src/lib/defaultOptions.js
+++ b/src/lib/defaultOptions.js
@@ -317,7 +317,7 @@ export default {
*
* @type {string}
*/
- plugins: '',
+ plugins: 'video',
/**
* z-index to set the editor container to. Needed for jQuery UI dialog.
@@ -350,7 +350,24 @@ export default {
*
* @type {Object}
*/
- parserOptions: { },
+ parserOptions: {
+ /**
+ * Parameters that will be added to YouTube frame tag
+ *
+ * @type {string}
+ */
+ youtubeParameters: 'width="560" height="315" frameborder="0" ' +
+ 'allowfullscreen',
+
+ /**
+ * Parameters that will be added to Facebook frame tag
+ *
+ * @type {string}
+ */
+ facebookParameters: 'width="560" height="315" ' +
+ 'style="border:none;overflow:hidden" scrolling="no" ' +
+ 'frameborder="0" allowTransparency="true" allowFullScreen="true"'
+ },
/**
* CSS that will be added to the to dropdown menu (eg. z-index)
diff --git a/src/lib/templates.js b/src/lib/templates.js
index 3cfa60e5a..4bac066c3 100644
--- a/src/lib/templates.js
+++ b/src/lib/templates.js
@@ -85,9 +85,9 @@ var _templates = {
'',
youtube:
- ''
+ ''
};
/**
diff --git a/src/plugins/video.js b/src/plugins/video.js
new file mode 100644
index 000000000..384f9d773
--- /dev/null
+++ b/src/plugins/video.js
@@ -0,0 +1,204 @@
+/**
+ * SCEditor Inline-Code Plugin for BBCode format
+ * http://www.sceditor.com/
+ *
+ * Copyright (C) 2011-2013, Sam Clarke (samclarke.com)
+ *
+ * SCEditor is licensed under the MIT license:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * @fileoverview SCEditor Video plugin
+ * This plugin replaces the youtube command with "add video" command, which
+ * will recognize youtube and facebook URLs (hopefully other URLs as well
+ * in the future) and generate the tags accordingly
+ * @author Alex Betis
+ */
+
+(function (sceditor) {
+ 'use strict';
+
+ sceditor.plugins.video = function () {
+ var base = this;
+
+ var utils = sceditor.utils;
+ var dom = sceditor.dom;
+
+ /**
+ * Private functions
+ * @private
+ */
+ var commandHandler;
+ var processYoutube;
+ var processFacebook;
+ var youtubeHtml;
+ var facebookHtml;
+
+ base.init = function () {
+ var opts = this.opts;
+
+ // Enable for BBCode only
+ if (opts.format && opts.format !== 'bbcode') {
+ return;
+ }
+
+ if (opts.toolbar === sceditor.defaultOptions.toolbar) {
+ opts.toolbar = opts.toolbar.replace(',image,',
+ ',image,video,');
+
+ opts.toolbar = opts.toolbar.replace(',youtube', '');
+ }
+
+ // Remove youtube command
+ sceditor.command.remove('youtube');
+
+ // Add new movie command
+ sceditor.command.set('video', {
+ exec: commandHandler,
+ txtExec: commandHandler,
+ tooltip: 'Insert a video (YouTube, Facebook)'
+ });
+
+ sceditor.formats.bbcode.set('facebook', {
+ allowsEmpty: true,
+ tags: {
+ iframe: {
+ 'data-facebook-id': null
+ }
+ },
+ format: function (element, content) {
+ var id = dom.attr(element, 'data-facebook-id');
+
+ return id ? '[facebook]' + id + '[/facebook]' : content;
+ },
+ html: function (token, attrs, content) {
+ return facebookHtml(this.opts.facebookParameters, content);
+ }
+ });
+ };
+
+ youtubeHtml = function (pOpts, id, time) {
+ return '';
+ };
+
+ facebookHtml = function (pOpts, id) {
+ return '';
+ };
+
+ processYoutube = function (editor, val) {
+ var pOpts = editor.opts.parserOptions;
+ var idMatch = val.match(/(?:v=|v\/|embed\/|youtu.be\/)(.{11})/);
+ var timeMatch = val.match(/[&|?](?:star)?t=((\d+[hms]?){1,3})/);
+ var time = 0;
+
+ if (timeMatch) {
+ utils.each(timeMatch[1].split(/[hms]/), function (i, val) {
+ if (val !== '') {
+ time = (time * 60) + Number(val);
+ }
+ });
+ }
+
+ if (idMatch && /^[a-zA-Z0-9_\-]{11}$/.test(idMatch[1])) {
+ var id = idMatch[1];
+
+ if (editor.sourceMode()) {
+ if (time === 0) {
+ editor.insertText('[youtube]' + id + '[/youtube]');
+ } else {
+ editor.insertText('[youtube=' + time + ']' + id +
+ '[/youtube]');
+ }
+ } else {
+ editor.wysiwygEditorInsertHtml(
+ youtubeHtml(pOpts.youtubeParameters, id, time));
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ processFacebook = function (editor, val) {
+ var pOpts = editor.opts.parserOptions;
+ var idMatch = val.match(/videos\/(\d+)+|v=(\d+)|vb.\d+\/(\d+)/);
+
+ if (idMatch && /^[a-zA-Z0-9]/.test(idMatch[1])) {
+ var id = idMatch[1];
+
+ if (editor.sourceMode()) {
+ editor.insertText('[facebook]' + id + '[/facebook]');
+ } else {
+ editor.wysiwygEditorInsertHtml(
+ facebookHtml(pOpts.facebookParameters, id));
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Function for the txtExec and exec properties
+ *
+ * @param {node} caller
+ * @private
+ */
+ commandHandler = function (caller) {
+ var editor = this;
+ var content = document.createElement('div');
+
+ var div;
+ var label;
+ var input;
+ var button;
+
+ div = document.createElement('div');
+ label = document.createElement('label');
+ label.setAttribute('for', 'link');
+ label.textContent = editor._('Video URL:');
+ input = document.createElement('input');
+ input.type = 'text';
+ input.id = 'link';
+ input.dir = 'ltr';
+ input.placeholder = 'https://';
+ div.appendChild(label);
+ div.appendChild(input);
+
+ content.appendChild(div);
+
+ div = document.createElement('div');
+ button = document.createElement('input');
+ button.type = 'button';
+ button.className = 'button';
+ button.value = editor._('Insert');
+ div.appendChild(button);
+
+ content.appendChild(div);
+
+ button.addEventListener('click', function (e) {
+ var val = input.value;
+ var done;
+
+ done = processYoutube(editor, val);
+ if (!done) {
+ processFacebook(editor, val);
+ }
+
+ editor.closeDropDown(true);
+ e.preventDefault();
+ });
+
+ editor.createDropDown(caller, 'insertlink', content);
+ };
+ };
+})(sceditor);
diff --git a/src/themes/icons/famfamfam.less b/src/themes/icons/famfamfam.less
index a0f6edc0c..53e9c4db6 100644
--- a/src/themes/icons/famfamfam.less
+++ b/src/themes/icons/famfamfam.less
@@ -7,49 +7,51 @@ div.sceditor-grip, .sceditor-button div {
width: 16px;
height: 16px;
}
-.sceditor-button-youtube div { background-position: 0px 0px; }
-.sceditor-button-link div { background-position: 0px -16px; }
-.sceditor-button-unlink div { background-position: 0px -32px; }
-.sceditor-button-underline div { background-position: 0px -48px; }
-.sceditor-button-time div { background-position: 0px -64px; }
-.sceditor-button-table div { background-position: 0px -80px; }
-.sceditor-button-superscript div { background-position: 0px -96px; }
-.sceditor-button-subscript div { background-position: 0px -112px; }
-.sceditor-button-strike div { background-position: 0px -128px; }
-.sceditor-button-source div { background-position: 0px -144px; }
-.sceditor-button-size div { background-position: 0px -160px; }
-.sceditor-button-rtl div { background-position: 0px -176px; }
-.sceditor-button-right div { background-position: 0px -192px; }
-.sceditor-button-removeformat div { background-position: 0px -208px; }
-.sceditor-button-quote div { background-position: 0px -224px; }
-.sceditor-button-print div { background-position: 0px -240px; }
-.sceditor-button-pastetext div { background-position: 0px -256px; }
-.sceditor-button-paste div { background-position: 0px -272px; }
-.sceditor-button-outdent div { background-position: 0px -288px; }
-.sceditor-button-orderedlist div { background-position: 0px -304px; }
-.sceditor-button-maximize div { background-position: 0px -320px; }
-.sceditor-button-ltr div { background-position: 0px -336px; }
-.sceditor-button-left div { background-position: 0px -352px; }
-.sceditor-button-justify div { background-position: 0px -368px; }
-.sceditor-button-italic div { background-position: 0px -384px; }
-.sceditor-button-indent div { background-position: 0px -400px; }
-.sceditor-button-image div { background-position: 0px -416px; }
-.sceditor-button-horizontalrule div { background-position: 0px -432px; }
-.sceditor-button-format div { background-position: 0px -448px; }
-.sceditor-button-font div { background-position: 0px -464px; }
-.sceditor-button-emoticon div { background-position: 0px -480px; }
-.sceditor-button-email div { background-position: 0px -496px; }
-.sceditor-button-date div { background-position: 0px -512px; }
-.sceditor-button-cut div { background-position: 0px -528px; }
-.sceditor-button-copy div { background-position: 0px -544px; }
-.sceditor-button-color div { background-position: 0px -560px; }
-.sceditor-button-code div { background-position: 0px -576px; }
-.sceditor-button-center div { background-position: 0px -592px; }
-.sceditor-button-bulletlist div { background-position: 0px -608px; }
-.sceditor-button-bold div { background-position: 0px -624px; }
+.sceditor-button-bold div { background-position: 0px 0px; }
+.sceditor-button-bulletlist div { background-position: 0px -16px; }
+.sceditor-button-center div { background-position: 0px -32px; }
+.sceditor-button-code div { background-position: 0px -48px; }
+.sceditor-button-color div { background-position: 0px -64px; }
+.sceditor-button-copy div { background-position: 0px -80px; }
+.sceditor-button-cut div { background-position: 0px -96px; }
+.sceditor-button-date div { background-position: 0px -112px; }
+.sceditor-button-email div { background-position: 0px -128px; }
+.sceditor-button-emoticon div { background-position: 0px -144px; }
+.sceditor-button-font div { background-position: 0px -160px; }
+.sceditor-button-format div { background-position: 0px -176px; }
+.sceditor-button-horizontalrule div { background-position: 0px -192px; }
+.sceditor-button-image div { background-position: 0px -208px; }
+.sceditor-button-indent div { background-position: 0px -224px; }
+.sceditor-button-italic div { background-position: 0px -240px; }
+.sceditor-button-justify div { background-position: 0px -256px; }
+.sceditor-button-left div { background-position: 0px -272px; }
+.sceditor-button-ltr div { background-position: 0px -288px; }
+.sceditor-button-maximize div { background-position: 0px -304px; }
+.sceditor-button-orderedlist div { background-position: 0px -320px; }
+.sceditor-button-outdent div { background-position: 0px -336px; }
+.sceditor-button-paste div { background-position: 0px -352px; }
+.sceditor-button-pastetext div { background-position: 0px -368px; }
+.sceditor-button-print div { background-position: 0px -384px; }
+.sceditor-button-quote div { background-position: 0px -400px; }
+.sceditor-button-removeformat div { background-position: 0px -416px; }
+.sceditor-button-right div { background-position: 0px -432px; }
+.sceditor-button-rtl div { background-position: 0px -448px; }
+.sceditor-button-size div { background-position: 0px -464px; }
+.sceditor-button-source div { background-position: 0px -480px; }
+.sceditor-button-strike div { background-position: 0px -496px; }
+.sceditor-button-subscript div { background-position: 0px -512px; }
+.sceditor-button-superscript div { background-position: 0px -528px; }
+.sceditor-button-table div { background-position: 0px -544px; }
+.sceditor-button-time div { background-position: 0px -560px; }
+.sceditor-button-underline div { background-position: 0px -576px; }
+.sceditor-button-unlink div { background-position: 0px -592px; }
+.sceditor-button-url div { background-position: 0px -608px; }
+.sceditor-button-video div { background-position: 0px -624px; }
+.sceditor-button-youtube div { background-position: 0px -640px; }
+
div.sceditor-grip {
- background-position: 0px -640px;
+ background-position: 0px -656px;
width: 10px;
height: 10px;
}
-.rtl div.sceditor-grip { background-position: 0px -650px; }
+.rtl div.sceditor-grip { background-position: 0px -666px; }
diff --git a/src/themes/icons/famfamfam.png b/src/themes/icons/famfamfam.png
index 488c72aca..fe99fce9a 100644
Binary files a/src/themes/icons/famfamfam.png and b/src/themes/icons/famfamfam.png differ
diff --git a/src/themes/icons/src/famfamfam/video.png b/src/themes/icons/src/famfamfam/video.png
new file mode 100644
index 000000000..40d681feb
Binary files /dev/null and b/src/themes/icons/src/famfamfam/video.png differ
diff --git a/tests/manual/debug/index.html b/tests/manual/debug/index.html
index 23e309d92..77d1c5c2a 100644
--- a/tests/manual/debug/index.html
+++ b/tests/manual/debug/index.html
@@ -20,6 +20,7 @@
+
diff --git a/tests/unit/formats/bbcode.js b/tests/unit/formats/bbcode.js
index b58aae2d7..42ceb8660 100644
--- a/tests/unit/formats/bbcode.js
+++ b/tests/unit/formats/bbcode.js
@@ -853,4 +853,15 @@ QUnit.test('YouTube', function (assert) {
this.htmlToBBCode(''),
'[youtube]xyz[/youtube]'
);
+
+ assert.equal(
+ this.htmlToBBCode(''),
+ '[youtube]xyz[/youtube]'
+ );
+
+ assert.equal(
+ this.htmlToBBCode(''),
+ '[youtube=123]xyz[/youtube]'
+ );
});
+
diff --git a/tests/unit/formats/bbcode.parser.js b/tests/unit/formats/bbcode.parser.js
index 21649dd9f..571c9a69c 100644
--- a/tests/unit/formats/bbcode.parser.js
+++ b/tests/unit/formats/bbcode.parser.js
@@ -628,7 +628,12 @@ QUnit.test('Attributes QuoteType custom', function (assert) {
QUnit.module('plugins/bbcode#Parser - To HTML', {
beforeEach: function () {
- this.parser = new sceditor.BBCodeParser({});
+ this.parser = new sceditor.BBCodeParser(
+ {
+ youtubeParameters: 'width="123" height="456" frameborder="0" ' +
+ 'allowfullscreen'
+ }
+ );
}
});
@@ -918,9 +923,18 @@ QUnit.test('Justify', function (assert) {
QUnit.test('YouTube', function (assert) {
assert.htmlEqual(
this.parser.toHTML('[youtube]xyz[/youtube]'),
- '