manual merge, still wip
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
@import (once) "../../customize/src/less2/include/toolbar.less";
|
||||
@import (once) '../../customize/src/less2/include/alertify.less';
|
||||
@import (once) '../../customize/src/less2/include/tokenfield.less';
|
||||
|
||||
.toolbar_main();
|
||||
.alertify_main();
|
||||
|
||||
// body
|
||||
&.cp-app-pad {
|
||||
.tokenfield_main();
|
||||
#cke_1_top {
|
||||
overflow: visible;
|
||||
padding: 0px;
|
||||
@@ -38,6 +40,6 @@
|
||||
display:none !important;
|
||||
}
|
||||
&.cp-app-pad .cp-toolbar-userlist-drawer {
|
||||
display:none;
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html class="cp-app-noscroll">
|
||||
<head>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.4" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
<script async data-bootload="/pad/inner.js" data-main="/common/sframe-boot.js?ver=1.5" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
</head>
|
||||
<body class="cp-app-pad">
|
||||
<textarea style="display:none" id="editor1" name="editor1"></textarea>
|
||||
|
||||
125
www/pad/inner.js
125
www/pad/inner.js
@@ -26,7 +26,9 @@ define([
|
||||
'/customize/messages.js',
|
||||
'/pad/links.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/common/media-tag.js',
|
||||
'/api/config',
|
||||
'/common/cryptpad-common.js',
|
||||
|
||||
'/bower_components/diff-dom/diffDOM.js',
|
||||
|
||||
@@ -42,7 +44,9 @@ define([
|
||||
Messages,
|
||||
Links,
|
||||
nThen,
|
||||
ApiConfig)
|
||||
MediaTag,
|
||||
ApiConfig,
|
||||
Cryptpad)
|
||||
{
|
||||
var DiffDom = window.diffDOM;
|
||||
|
||||
@@ -78,9 +82,18 @@ define([
|
||||
el.getAttribute('class').split(' ').indexOf('non-realtime') !== -1);
|
||||
};
|
||||
|
||||
/* catch `type="_moz"` before it goes over the wire */
|
||||
var brFilter = function (hj) {
|
||||
if (hj[1].type === '_moz') { hj[1].type = undefined; }
|
||||
var hjsonFilters = function (hj) {
|
||||
/* catch `type="_moz"` before it goes over the wire */
|
||||
var brFilter = function (hj) {
|
||||
if (hj[1].type === '_moz') { hj[1].type = undefined; }
|
||||
return hj;
|
||||
};
|
||||
var mediatagContentFilter = function (hj) {
|
||||
if (hj[0] === 'MEDIA-TAG') { hj[2] = []; }
|
||||
return hj;
|
||||
};
|
||||
brFilter(hj);
|
||||
mediatagContentFilter(hj);
|
||||
return hj;
|
||||
};
|
||||
|
||||
@@ -90,11 +103,11 @@ define([
|
||||
|
||||
var forbiddenTags = [
|
||||
'SCRIPT',
|
||||
'IFRAME',
|
||||
//'IFRAME',
|
||||
'OBJECT',
|
||||
'APPLET',
|
||||
'VIDEO',
|
||||
'AUDIO'
|
||||
//'VIDEO',
|
||||
//'AUDIO'
|
||||
];
|
||||
|
||||
var getHTML = function (inner) {
|
||||
@@ -308,11 +321,38 @@ define([
|
||||
};
|
||||
|
||||
if (!framework.isReadOnly()) {
|
||||
console.log('\n\n\n\n\nREGISTER\n\n\n\n\n');
|
||||
framework.onEditableChange(function () {
|
||||
console.log("Editable change");
|
||||
var locked = framework.isLocked();
|
||||
$(inner).css({ 'background-color': ((locked) ? '#aaa' : '') });
|
||||
inner.setAttribute('contenteditable', !locked);
|
||||
});
|
||||
|
||||
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(window.CKEDITOR.dom.element.createFromHtml(mt));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
framework._.sfCommon.initFilePicker(fileDialogCfg);
|
||||
window.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']
|
||||
};
|
||||
framework._.sfCommon.openFilePicker(pickerCfg);
|
||||
}).appendTo(framework._.toolbar.$rightside);
|
||||
|
||||
var $tags = framework._.sfCommon.createButton('hashtag', true);
|
||||
framework._.toolbar.$rightside.append($tags);
|
||||
}
|
||||
|
||||
framework.setTitleRecommender(function () {
|
||||
@@ -328,15 +368,65 @@ define([
|
||||
|
||||
var DD = new DiffDom(mkDiffOptions(cursor, framework.isReadOnly()));
|
||||
|
||||
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');
|
||||
Array.prototype.slice.call(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');
|
||||
console.log(Array.prototype.slice.call(tags));
|
||||
Array.prototype.slice.call(tags).forEach(function (el) {
|
||||
MediaTag(el);
|
||||
$(el).on('keydown', function (e) {
|
||||
if ([8,46].indexOf(e.which) !== -1) {
|
||||
$(el).remove();
|
||||
framework.localChange();
|
||||
}
|
||||
});
|
||||
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!
|
||||
framework.onContentUpdate(function (hjson) {
|
||||
if (!Array.isArray(hjson)) {
|
||||
var errorText = Messages.typeError;
|
||||
Cryptpad.errorLoadingScreen(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
var userDocStateDom = hjsonToDom(hjson);
|
||||
|
||||
userDocStateDom.setAttribute("contenteditable",
|
||||
inner.getAttribute('contenteditable'));
|
||||
|
||||
restoreMediaTags(userDocStateDom);
|
||||
var patch = (DD).diff(inner, userDocStateDom);
|
||||
(DD).apply(inner, patch);
|
||||
displayMediaTags(inner);
|
||||
if (framework.isReadOnly()) {
|
||||
var $links = $(inner).find('a');
|
||||
// off so that we don't end up with multiple identical handlers
|
||||
@@ -345,7 +435,7 @@ define([
|
||||
});
|
||||
|
||||
framework.setContentGetter(function () {
|
||||
return Hyperjson.fromDOM(inner, isNotMagicLine, brFilter);
|
||||
return Hyperjson.fromDOM(inner, isNotMagicLine, hjsonFilters);
|
||||
});
|
||||
|
||||
$bar.find('#cke_1_toolbar_collapser').hide();
|
||||
@@ -365,11 +455,22 @@ define([
|
||||
|
||||
editor.focus();
|
||||
if (newPad) {
|
||||
documentBody.innerHTML = Messages.initialState;
|
||||
cursor.setToEnd();
|
||||
} else if (framework.isReadOnly()) {
|
||||
cursor.setToStart();
|
||||
}
|
||||
var fmConfig = {
|
||||
ckeditor: editor,
|
||||
body: $('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
var parsed = Cryptpad.parsePadUrl(data.url);
|
||||
var hexFileName = Cryptpad.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
var mt = '<media-tag contenteditable="false" src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '" tabindex="1"></media-tag>';
|
||||
editor.insertElement(window.CKEDITOR.dom.element.createFromHtml(mt));
|
||||
}
|
||||
};
|
||||
window.APP.FM = framework._.sfCommon.createFileManager(fmConfig);
|
||||
});
|
||||
|
||||
framework.onDefaultContentNeeded(function () {
|
||||
@@ -457,12 +558,18 @@ define([
|
||||
}
|
||||
// Used in ckeditor-config.js
|
||||
Ckeditor.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs;
|
||||
Ckeditor.plugins.addExternal('mediatag','/pad/', 'mediatag-plugin.js');
|
||||
module.ckeditor = editor = Ckeditor.replace('editor1', {
|
||||
customConfig: '/customize/ckeditor-config.js',
|
||||
});
|
||||
editor.on('instanceReady', waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
Framework.create({}, waitFor(function (fw) { window.APP.framework = framework = fw; }));
|
||||
editor.plugins.mediatag.translations = {
|
||||
title: Messages.pad_mediatagTitle,
|
||||
width: Messages.pad_mediatagWidth,
|
||||
height: Messages.pad_mediatagHeight
|
||||
};
|
||||
Links.addSupportForOpeningLinksInNewTab(Ckeditor)({editor: editor});
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
andThen2(editor, Ckeditor, framework);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
define(['/common/cryptpad-common.js'], function (Cryptpad) {
|
||||
define(['/customize/messages.js'], function (Messages) {
|
||||
// Adds a context menu entry to open the selected link in a new tab.
|
||||
// See https://github.com/xwiki-contrib/application-ckeditor/commit/755d193497bf23ed874d874b4ae92fbee887fc10
|
||||
var Messages = Cryptpad.Messages;
|
||||
return {
|
||||
addSupportForOpeningLinksInNewTab : function (Ckeditor) {
|
||||
// Returns the DOM element of the active (currently focused) link. It has also support for linked image widgets.
|
||||
|
||||
60
www/pad/mediatag-plugin-dialog.js
Normal file
60
www/pad/mediatag-plugin-dialog.js
Normal file
@@ -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' );
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
181
www/pad/mediatag-plugin.js
Normal file
181
www/pad/mediatag-plugin.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @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 img{' +
|
||||
'vertical-align: top;' +
|
||||
'}' +
|
||||
'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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user