Mediatag integration in pads

pull/1/head
yflory 7 years ago
parent 96d0456f31
commit 974658453c

@ -10,7 +10,7 @@ CKEDITOR.editorConfig = function( config ) {
// document itself and causes problems when it's sent across the wire and reflected back // document itself and causes problems when it's sent across the wire and reflected back
config.removePlugins= 'resize,elementspath'; config.removePlugins= 'resize,elementspath';
config.resize_enabled= false; //bottom-bar config.resize_enabled= false; //bottom-bar
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify'; config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag';
config.toolbarGroups= [ config.toolbarGroups= [
// {"name":"clipboard","groups":["clipboard","undo"]}, // {"name":"clipboard","groups":["clipboard","undo"]},
//{"name":"editing","groups":["find","selection"]}, //{"name":"editing","groups":["find","selection"]},

@ -32,6 +32,7 @@ define([
'/pad/links.js', '/pad/links.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/media-tag.js',
'/api/config', '/api/config',
'/bower_components/file-saver/FileSaver.min.js', '/bower_components/file-saver/FileSaver.min.js',
@ -55,6 +56,7 @@ define([
Links, Links,
nThen, nThen,
SFCommon, SFCommon,
MediaTag,
ApiConfig) ApiConfig)
{ {
var saveAs = window.saveAs; var saveAs = window.saveAs;
@ -113,6 +115,15 @@ define([
if (hj[1].type === '_moz') { hj[1].type = undefined; } if (hj[1].type === '_moz') { hj[1].type = undefined; }
return hj; return hj;
}; };
var mediatagContentFilter = function (hj) {
if (hj[0] === 'MEDIA-TAG') { hj[2] = []; }
return hj;
};
var hjsonFilters = function (hj) {
brFilter(hj);
mediatagContentFilter(hj);
return hj;
};
var onConnectError = function () { var onConnectError = function () {
Cryptpad.errorLoadingScreen(Messages.websocketError); Cryptpad.errorLoadingScreen(Messages.websocketError);
@ -124,11 +135,11 @@ define([
var forbiddenTags = [ var forbiddenTags = [
'SCRIPT', 'SCRIPT',
'IFRAME', //'IFRAME',
'OBJECT', 'OBJECT',
'APPLET', 'APPLET',
'VIDEO', //'VIDEO',
'AUDIO' //'AUDIO'
]; ];
var getHTML = function (inner) { var getHTML = function (inner) {
@ -359,6 +370,48 @@ define([
var DD = new DiffDom(mkDiffOptions(cursor, readOnly)); var DD = new DiffDom(mkDiffOptions(cursor, readOnly));
var mediaMap = {};
var restoreMediaTags = function (tempDom) {
var pattern = /(<media-tag contenteditable="false" data-crypto-key="([^"]*)" src="([^"]*)" tabindex="1">)<\/media-tag>/i;
var tags = tempDom.querySelectorAll('media-tag:empty');
Cryptpad.slice(tags).forEach(function (tag) {
if (pattern.length !== 4) { return; }
var src = pattern[3];
if (mediaMap[src]) {
mediaMap[src].forEach(function (n) {
tag.appendChild(n);
});
}
});
};
var displayMediaTags = function (dom) {
setTimeout(function () { // Just in case
var tags = dom.querySelectorAll('media-tag:empty');
Cryptpad.slice(tags).forEach(function (el) {
MediaTag(el);
$(el).on('keydown', function (e) {
if ([8,46].indexOf(e.which) !== -1) {
$(el).remove();
onLocal();
}
});
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
var list_values = [].slice.call(el.children);
mediaMap[el.getAttribute('src')] = list_values;
}
});
});
observer.observe(el, {
attributes: false,
childList: true,
characterData: false
});
});
});
};
// apply patches, and try not to lose the cursor in the process! // apply patches, and try not to lose the cursor in the process!
var applyHjson = function (shjson) { var applyHjson = function (shjson) {
var userDocStateDom = hjsonToDom(JSON.parse(shjson)); var userDocStateDom = hjsonToDom(JSON.parse(shjson));
@ -368,8 +421,10 @@ define([
} else if (readOnly) { } else if (readOnly) {
userDocStateDom.removeAttribute("contenteditable"); userDocStateDom.removeAttribute("contenteditable");
} }
restoreMediaTags(userDocStateDom);
var patch = (DD).diff(inner, userDocStateDom); var patch = (DD).diff(inner, userDocStateDom);
(DD).apply(inner, patch); (DD).apply(inner, patch);
displayMediaTags(inner);
if (readOnly) { if (readOnly) {
var $links = $(inner).find('a'); var $links = $(inner).find('a');
// off so that we don't end up with multiple identical handlers // off so that we don't end up with multiple identical handlers
@ -378,7 +433,7 @@ define([
}; };
var stringifyDOM = module.stringifyDOM = function (dom) { var stringifyDOM = module.stringifyDOM = function (dom) {
var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, brFilter); var hjson = Hyperjson.fromDOM(dom, isNotMagicLine, hjsonFilters);
hjson[3] = { hjson[3] = {
metadata: metadataMgr.getMetadataLazy() metadata: metadataMgr.getMetadataLazy()
}; };
@ -617,6 +672,30 @@ define([
}; };
var $forgetPad = common.createButton('forget', true, {}, forgetCb); var $forgetPad = common.createButton('forget', true, {}, forgetCb);
$rightside.append($forgetPad); $rightside.append($forgetPad);
if (!readOnly) {
var fileDialogCfg = {
onSelect: function (data) {
if (data.type === 'file') {
var mt = '<media-tag contenteditable="false" src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '" tabindex="1"></media-tag>';
editor.insertElement(CKEDITOR.dom.element.createFromHtml(mt));
return;
}
}
};
common.initFilePicker(fileDialogCfg);
APP.$mediaTagButton = $('<button>', {
title: Messages.filePickerButton,
'class': 'cp-toolbar-rightside-button fa fa-picture-o',
style: 'font-size: 17px'
}).click(function () {
var pickerCfg = {
types: ['file'],
where: ['root']
};
common.openFilePicker(pickerCfg);
}).appendTo($rightside);
}
}; };
// this should only ever get called once, when the chain syncs // this should only ever get called once, when the chain syncs
@ -713,6 +792,7 @@ define([
// stringify the json and send it into chainpad // stringify the json and send it into chainpad
var shjson = stringifyDOM(inner); var shjson = stringifyDOM(inner);
displayMediaTags(inner);
module.patchText(shjson); module.patchText(shjson);
if (module.realtime.getUserDoc() !== shjson) { if (module.realtime.getUserDoc() !== shjson) {
@ -794,11 +874,17 @@ define([
} }
// Used in ckeditor-config.js // Used in ckeditor-config.js
Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs; Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
module.ckeditor = editor = Ckeditor.replace('editor1', { module.ckeditor = editor = Ckeditor.replace('editor1', {
customConfig: '/customize/ckeditor-config.js', customConfig: '/customize/ckeditor-config.js',
}); });
editor.on('instanceReady', waitFor()); editor.on('instanceReady', waitFor());
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
editor.plugins.mediatag.translations = {
title: 'TODO: TITLE',
width: 'TODO: width',
height: 'TODO: height'
};
/*if (Ckeditor.env.safari) { /*if (Ckeditor.env.safari) {
var fixIframe = function () { var fixIframe = function () {
$('iframe.cke_wysiwyg_frame').height($('#cke_1_contents').height()); $('iframe.cke_wysiwyg_frame').height($('#cke_1_contents').height());

@ -0,0 +1,60 @@
CKEDITOR.dialog.add('mediatag', function (editor) {
var Messages = editor.plugins.mediatag.translations;
return {
title: Messages.title,
minWidth: 400,
minHeight: 200,
contents: [
{
id: 'tab-basic',
label: Messages.title,
elements: [
{
type: 'text',
id: 'width',
label: Messages.width,
},
{
type: 'text',
id: 'height',
label: Messages.height,
}
]
},
],
onShow: function () {
var el = editor.plugins.mediatag.clicked;
var rect = el.getClientRect();
var dialog = this.parts.contents.$;
var inputs = dialog.querySelectorAll('input');
var wInput = inputs[0];
var hInput = inputs[1];
wInput.value = Math.round(rect.width);
hInput.value = Math.round(rect.height);
},
onOk: function() {
var dialog = this;
var el = editor.plugins.mediatag.clicked;
var dialog = this.parts.contents.$;
var inputs = dialog.querySelectorAll('input');
var wInput = inputs[0];
var hInput = inputs[1];
window.setTimeout(function () {
if (wInput.value === "") {
el.removeAttribute('width');
el.removeStyle('width');
} else {
el.setSize('width', parseInt(wInput.value));
}
if (hInput.value === "") {
el.removeAttribute('height');
el.removeStyle('height');
} else {
el.setSize('height', parseInt(hInput.value));
}
editor.fire( 'change' );
});
}
};
});

@ -0,0 +1,178 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview The Image plugin.
*/
( function() {
CKEDITOR.plugins.add( 'mediatag', {
requires: 'dialog',
//icons: 'image',
//hidpi: true,
onLoad: function () {
CKEDITOR.addCss(
'media-tag{' +
'display:inline-block;' +
'}' +
'media-tag.selected{' +
'border: 1px solid black;' +
'}' +
'media-tag iframe{' +
'border: 6px solid #eee;' +
'}' +
'media-tag *{' +
'width:100%; height:100%;' +
'}');
},
init: function( editor ) {
var pluginName = 'mediatag';
// Register the dialog.
CKEDITOR.dialog.add( pluginName, this.path + 'mediatag-plugin-dialog.js' );
var allowed = 'media-tag[!data-crypto-key,!src,contenteditable,width,height]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}',
required = 'media-tag[data-crypto-key,src]';
// Register the command.
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName, {
allowedContent: allowed,
requiredContent: required,
contentTransformations: [
[ 'media-tag{width}: sizeToStyle', 'media-tag[width]: sizeToAttribute' ],
[ 'media-tag{float}: alignmentToStyle', 'media-tag[align]: alignmentToAttribute' ]
]
} ) );
var isMediaTag = function (el) {
if (el.is('media-tag')) { return el; }
var mt = el.getParents().slice().filter(function (p) {
return p.is('media-tag');
});
if (mt.length !== 1) { return; }
return mt[0];
};
editor.on('doubleclick', function (evt) {
var element = evt.data.element;
var mt = isMediaTag(element);
if (mt && !element.data('cke-realelement')) {
editor.plugins.mediatag.clicked = mt;
evt.data.dialog = 'mediatag';
}
});
// If the "contextmenu" plugin is loaded, register the listeners.
if (editor.contextMenu) {
editor.contextMenu.addListener(function (element) {
if (getSelectedMediatag(editor, element)) {
return { mediatag: CKEDITOR.TRISTATE_OFF };
}
});
}
},
afterInit: function( editor ) {
// Customize the behavior of the alignment commands. (http://dev.ckeditor.com/ticket/7430)
setupAlignCommand('left');
setupAlignCommand('right');
setupAlignCommand('center');
setupAlignCommand('block');
function setupAlignCommand (value) {
var command = editor.getCommand('justify' + value);
if (command) {
if (value === 'left' || value === 'right') {
command.on('exec', function (evt) {
var img = getSelectedMediatag(editor), align;
if (img) {
align = getMediatagAlignment(img);
if (align === value) {
img.removeStyle('float');
// Remove "align" attribute when necessary.
if (value === getMediatagAlignment(img))
img.removeAttribute( 'align' );
} else {
img.setStyle( 'float', value );
}
evt.cancel();
}
} );
}
command.on('refresh', function (evt) {
var img = getSelectedMediatag(editor), align;
if (img) {
align = getMediatagAlignment(img);
this.setState(
(align === value) ? CKEDITOR.TRISTATE_ON : ( value === 'right' || value === 'left' ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
evt.cancel();
}
} );
}
}
}
} );
function getSelectedMediatag (editor, element) {
if (!element) {
var sel = editor.getSelection();
element = sel.getSelectedElement();
}
if (element && element.is('media-tag') && !element.data('cke-realelement')
&& !element.isReadOnly()) {
return element;
}
}
function getMediatagAlignment (element) {
var align = element.getStyle('float');
if (align === 'inherit' || align === 'none') {
align = 0;
}
if (!align) {
align = element.getAttribute('align');
}
return align;
}
} )();
/**
* Determines whether dimension inputs should be automatically filled when the image URL changes in the Image plugin dialog window.
*
* config.image_prefillDimensions = false;
*
* @since 4.5
* @cfg {Boolean} [image_prefillDimensions=true]
* @member CKEDITOR.config
*/
/**
* Whether to remove links when emptying the link URL field in the Image dialog window.
*
* config.image_removeLinkByEmptyURL = false;
*
* @cfg {Boolean} [image_removeLinkByEmptyURL=true]
* @member CKEDITOR.config
*/
CKEDITOR.config.mediatag_removeLinkByEmptyURL = true;
/**
* Padding text to set off the image in the preview area.
*
* config.image_previewText = CKEDITOR.tools.repeat( '___ ', 100 );
*
* @cfg {String} [image_previewText='Lorem ipsum dolor...' (placeholder text)]
* @member CKEDITOR.config
*/
Loading…
Cancel
Save