scrivito_editors 1.3.1 → 1.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/scrivito_editors/image_editor.js +1 -1
- data/app/assets/javascripts/scrivito_editors/medium_editor.js +1 -1
- data/app/assets/javascripts/scrivito_editors/referencelist_editor.js +1 -1
- data/vendor/assets/javascripts/medium-editor.js +557 -171
- data/vendor/assets/stylesheets/medium-editor.css +9 -8
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c20b548f95ef5b6cc55ae5d0f84bc1630983ff1
|
4
|
+
data.tar.gz: 0f80d14e64a84c337fd5c24e44ac47ec53032e5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e8058e3038e79dc4fcb5f2a439d1efac42e490707b8724aa29b1fab597cba69721ef5afcb737ea92ca2ecfc545b02e3cb2dc3b73e7d5a8b0f14ca1323f71c12
|
7
|
+
data.tar.gz: 994a4ef961e50e17b0c7e39fa45a52286ac4e0d3c999bf38ed8e8ffb65b2e40991f5f5e58e1f74c3132ea1a81f20d7713322dc01398a621dd1d7a92fb3238cd4
|
@@ -55,7 +55,7 @@
|
|
55
55
|
var container, image;
|
56
56
|
container = $(".image_kit_start");
|
57
57
|
image = new Image();
|
58
|
-
return element.scrivito('content').
|
58
|
+
return element.scrivito('content').noCacheUrl().then(function(no_cache_url) {
|
59
59
|
image.crossOrigin = "Anonymous";
|
60
60
|
image.src = no_cache_url;
|
61
61
|
image.onerror = function() {
|
@@ -75,8 +75,8 @@
|
|
75
75
|
var i, len, objId, objIds, ul;
|
76
76
|
cmsField.empty();
|
77
77
|
objIds = cmsField.scrivito('content');
|
78
|
+
ul = $('<ul></ul>').appendTo(cmsField);
|
78
79
|
if (objIds.length) {
|
79
|
-
ul = $('<ul></ul>').appendTo(cmsField);
|
80
80
|
for (i = 0, len = objIds.length; i < len; i++) {
|
81
81
|
objId = objIds[i];
|
82
82
|
ul.append(renderItem(objId));
|
@@ -454,6 +454,7 @@ MediumEditor.extensions = {};
|
|
454
454
|
isMac: (window.navigator.platform.toUpperCase().indexOf('MAC') >= 0),
|
455
455
|
|
456
456
|
// https://github.com/jashkenas/underscore
|
457
|
+
// Lonely letter MUST USE the uppercase code
|
457
458
|
keyCode: {
|
458
459
|
BACKSPACE: 8,
|
459
460
|
TAB: 9,
|
@@ -462,7 +463,8 @@ MediumEditor.extensions = {};
|
|
462
463
|
SPACE: 32,
|
463
464
|
DELETE: 46,
|
464
465
|
K: 75, // K keycode, and not k
|
465
|
-
M: 77
|
466
|
+
M: 77,
|
467
|
+
V: 86
|
466
468
|
},
|
467
469
|
|
468
470
|
/**
|
@@ -897,8 +899,7 @@ MediumEditor.extensions = {};
|
|
897
899
|
range = range.cloneRange();
|
898
900
|
range.setStartAfter(lastNode);
|
899
901
|
range.collapse(true);
|
900
|
-
selection.
|
901
|
-
selection.addRange(range);
|
902
|
+
MediumEditor.selection.selectRange(doc, range);
|
902
903
|
}
|
903
904
|
res = true;
|
904
905
|
}
|
@@ -1457,6 +1458,17 @@ MediumEditor.extensions = {};
|
|
1457
1458
|
} else {
|
1458
1459
|
el.parentNode.removeChild(el);
|
1459
1460
|
}
|
1461
|
+
},
|
1462
|
+
|
1463
|
+
guid: function () {
|
1464
|
+
function _s4() {
|
1465
|
+
return Math
|
1466
|
+
.floor((1 + Math.random()) * 0x10000)
|
1467
|
+
.toString(16)
|
1468
|
+
.substring(1);
|
1469
|
+
}
|
1470
|
+
|
1471
|
+
return _s4() + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + _s4() + _s4();
|
1460
1472
|
}
|
1461
1473
|
};
|
1462
1474
|
|
@@ -1938,9 +1950,7 @@ MediumEditor.extensions = {};
|
|
1938
1950
|
range = this.importSelectionMoveCursorPastAnchor(selectionState, range);
|
1939
1951
|
}
|
1940
1952
|
|
1941
|
-
|
1942
|
-
sel.removeAllRanges();
|
1943
|
-
sel.addRange(range);
|
1953
|
+
this.selectRange(doc, range);
|
1944
1954
|
},
|
1945
1955
|
|
1946
1956
|
// Utility method called from importSelection only
|
@@ -2333,16 +2343,12 @@ MediumEditor.extensions = {};
|
|
2333
2343
|
},
|
2334
2344
|
|
2335
2345
|
selectNode: function (node, doc) {
|
2336
|
-
var range = doc.createRange()
|
2337
|
-
sel = doc.getSelection();
|
2338
|
-
|
2346
|
+
var range = doc.createRange();
|
2339
2347
|
range.selectNodeContents(node);
|
2340
|
-
|
2341
|
-
sel.addRange(range);
|
2348
|
+
this.selectRange(doc, range);
|
2342
2349
|
},
|
2343
2350
|
|
2344
2351
|
select: function (doc, startNode, startOffset, endNode, endOffset) {
|
2345
|
-
doc.getSelection().removeAllRanges();
|
2346
2352
|
var range = doc.createRange();
|
2347
2353
|
range.setStart(startNode, startOffset);
|
2348
2354
|
if (endNode) {
|
@@ -2350,7 +2356,7 @@ MediumEditor.extensions = {};
|
|
2350
2356
|
} else {
|
2351
2357
|
range.collapse(true);
|
2352
2358
|
}
|
2353
|
-
|
2359
|
+
this.selectRange(doc, range);
|
2354
2360
|
return range;
|
2355
2361
|
},
|
2356
2362
|
|
@@ -2387,6 +2393,13 @@ MediumEditor.extensions = {};
|
|
2387
2393
|
return selection.getRangeAt(0);
|
2388
2394
|
},
|
2389
2395
|
|
2396
|
+
selectRange: function (ownerDocument, range) {
|
2397
|
+
var selection = ownerDocument.getSelection();
|
2398
|
+
|
2399
|
+
selection.removeAllRanges();
|
2400
|
+
selection.addRange(range);
|
2401
|
+
},
|
2402
|
+
|
2390
2403
|
// http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi
|
2391
2404
|
// by You
|
2392
2405
|
getSelectionStart: function (ownerDocument) {
|
@@ -2417,18 +2430,26 @@ MediumEditor.extensions = {};
|
|
2417
2430
|
|
2418
2431
|
// Helpers for event handling
|
2419
2432
|
|
2420
|
-
attachDOMEvent: function (
|
2421
|
-
|
2422
|
-
|
2433
|
+
attachDOMEvent: function (targets, event, listener, useCapture) {
|
2434
|
+
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets;
|
2435
|
+
|
2436
|
+
Array.prototype.forEach.call(targets, function (target) {
|
2437
|
+
target.addEventListener(event, listener, useCapture);
|
2438
|
+
this.events.push([target, event, listener, useCapture]);
|
2439
|
+
}.bind(this));
|
2423
2440
|
},
|
2424
2441
|
|
2425
|
-
detachDOMEvent: function (
|
2426
|
-
var index
|
2427
|
-
|
2428
|
-
|
2429
|
-
|
2430
|
-
|
2431
|
-
|
2442
|
+
detachDOMEvent: function (targets, event, listener, useCapture) {
|
2443
|
+
var index, e;
|
2444
|
+
targets = MediumEditor.util.isElement(targets) || [window, document].indexOf(targets) > -1 ? [targets] : targets;
|
2445
|
+
|
2446
|
+
Array.prototype.forEach.call(targets, function (target) {
|
2447
|
+
index = this.indexOfListener(target, event, listener, useCapture);
|
2448
|
+
if (index !== -1) {
|
2449
|
+
e = this.events.splice(index, 1)[0];
|
2450
|
+
e[0].removeEventListener(e[1], e[2], e[3]);
|
2451
|
+
}
|
2452
|
+
}.bind(this));
|
2432
2453
|
},
|
2433
2454
|
|
2434
2455
|
indexOfListener: function (target, event, listener, useCapture) {
|
@@ -2450,6 +2471,30 @@ MediumEditor.extensions = {};
|
|
2450
2471
|
}
|
2451
2472
|
},
|
2452
2473
|
|
2474
|
+
detachAllEventsFromElement: function (element) {
|
2475
|
+
var filtered = this.events.filter(function (e) {
|
2476
|
+
return e && e[0].getAttribute && e[0].getAttribute('medium-editor-index') === element.getAttribute('medium-editor-index');
|
2477
|
+
});
|
2478
|
+
|
2479
|
+
for (var i = 0, len = filtered.length; i < len; i++) {
|
2480
|
+
var e = filtered[i];
|
2481
|
+
this.detachDOMEvent(e[0], e[1], e[2], e[3]);
|
2482
|
+
}
|
2483
|
+
},
|
2484
|
+
|
2485
|
+
// Attach all existing handlers to a new element
|
2486
|
+
attachAllEventsToElement: function (element) {
|
2487
|
+
if (this.listeners['editableInput']) {
|
2488
|
+
this.contentCache[element.getAttribute('medium-editor-index')] = element.innerHTML;
|
2489
|
+
}
|
2490
|
+
|
2491
|
+
if (this.eventsCache) {
|
2492
|
+
this.eventsCache.forEach(function (e) {
|
2493
|
+
this.attachDOMEvent(element, e['name'], e['handler'].bind(this));
|
2494
|
+
}, this);
|
2495
|
+
}
|
2496
|
+
},
|
2497
|
+
|
2453
2498
|
enableCustomEvent: function (event) {
|
2454
2499
|
if (this.disabledEvents[event] !== undefined) {
|
2455
2500
|
delete this.disabledEvents[event];
|
@@ -2641,15 +2686,15 @@ MediumEditor.extensions = {};
|
|
2641
2686
|
break;
|
2642
2687
|
case 'editableInput':
|
2643
2688
|
// setup cache for knowing when the content has changed
|
2644
|
-
this.contentCache =
|
2689
|
+
this.contentCache = {};
|
2645
2690
|
this.base.elements.forEach(function (element) {
|
2646
2691
|
this.contentCache[element.getAttribute('medium-editor-index')] = element.innerHTML;
|
2692
|
+
}, this);
|
2647
2693
|
|
2648
|
-
|
2649
|
-
|
2650
|
-
|
2651
|
-
|
2652
|
-
}.bind(this));
|
2694
|
+
// Attach to the 'oninput' event, handled correctly by most browsers
|
2695
|
+
if (this.InputEventOnContenteditableSupported) {
|
2696
|
+
this.attachToEachElement('input', this.handleInput);
|
2697
|
+
}
|
2653
2698
|
|
2654
2699
|
// For browsers which don't support the input event on contenteditable (IE)
|
2655
2700
|
// we'll attach to 'selectionchange' on the document and 'keypress' on the editables
|
@@ -2719,9 +2764,26 @@ MediumEditor.extensions = {};
|
|
2719
2764
|
},
|
2720
2765
|
|
2721
2766
|
attachToEachElement: function (name, handler) {
|
2767
|
+
// build our internal cache to know which element got already what handler attached
|
2768
|
+
if (!this.eventsCache) {
|
2769
|
+
this.eventsCache = [];
|
2770
|
+
}
|
2771
|
+
|
2722
2772
|
this.base.elements.forEach(function (element) {
|
2723
2773
|
this.attachDOMEvent(element, name, handler.bind(this));
|
2724
2774
|
}, this);
|
2775
|
+
|
2776
|
+
this.eventsCache.push({ 'name': name, 'handler': handler });
|
2777
|
+
},
|
2778
|
+
|
2779
|
+
cleanupElement: function (element) {
|
2780
|
+
var index = element.getAttribute('medium-editor-index');
|
2781
|
+
if (index) {
|
2782
|
+
this.detachAllEventsFromElement(element);
|
2783
|
+
if (this.contentCache) {
|
2784
|
+
delete this.contentCache[index];
|
2785
|
+
}
|
2786
|
+
}
|
2725
2787
|
},
|
2726
2788
|
|
2727
2789
|
focusElement: function (element) {
|
@@ -2793,12 +2855,14 @@ MediumEditor.extensions = {};
|
|
2793
2855
|
}
|
2794
2856
|
// An event triggered which signifies that the user may have changed someting
|
2795
2857
|
// Look in our cache of input for the contenteditables to see if something changed
|
2796
|
-
var index = target.getAttribute('medium-editor-index')
|
2797
|
-
|
2858
|
+
var index = target.getAttribute('medium-editor-index'),
|
2859
|
+
html = target.innerHTML;
|
2860
|
+
|
2861
|
+
if (html !== this.contentCache[index]) {
|
2798
2862
|
// The content has changed since the last time we checked, fire the event
|
2799
2863
|
this.triggerCustomEvent('editableInput', eventObj, target);
|
2800
2864
|
}
|
2801
|
-
this.contentCache[index] =
|
2865
|
+
this.contentCache[index] = html;
|
2802
2866
|
},
|
2803
2867
|
|
2804
2868
|
handleDocumentSelectionChange: function (event) {
|
@@ -3679,12 +3743,12 @@ MediumEditor.extensions = {};
|
|
3679
3743
|
targetCheckbox = this.getAnchorTargetCheckbox(),
|
3680
3744
|
buttonCheckbox = this.getAnchorButtonCheckbox();
|
3681
3745
|
|
3682
|
-
opts = opts || {
|
3746
|
+
opts = opts || { value: '' };
|
3683
3747
|
// TODO: This is for backwards compatability
|
3684
3748
|
// We don't need to support the 'string' argument in 6.0.0
|
3685
3749
|
if (typeof opts === 'string') {
|
3686
3750
|
opts = {
|
3687
|
-
|
3751
|
+
value: opts
|
3688
3752
|
};
|
3689
3753
|
}
|
3690
3754
|
|
@@ -3693,7 +3757,7 @@ MediumEditor.extensions = {};
|
|
3693
3757
|
MediumEditor.extensions.form.prototype.showForm.apply(this);
|
3694
3758
|
this.setToolbarPosition();
|
3695
3759
|
|
3696
|
-
input.value = opts.
|
3760
|
+
input.value = opts.value;
|
3697
3761
|
input.focus();
|
3698
3762
|
|
3699
3763
|
// If we have a target checkbox, we want it to be checked/unchecked
|
@@ -3730,11 +3794,11 @@ MediumEditor.extensions = {};
|
|
3730
3794
|
var targetCheckbox = this.getAnchorTargetCheckbox(),
|
3731
3795
|
buttonCheckbox = this.getAnchorButtonCheckbox(),
|
3732
3796
|
opts = {
|
3733
|
-
|
3797
|
+
value: this.getInput().value.trim()
|
3734
3798
|
};
|
3735
3799
|
|
3736
3800
|
if (this.linkValidation) {
|
3737
|
-
opts.
|
3801
|
+
opts.value = this.checkLinkFormat(opts.value);
|
3738
3802
|
}
|
3739
3803
|
|
3740
3804
|
opts.target = '_self';
|
@@ -3771,7 +3835,7 @@ MediumEditor.extensions = {};
|
|
3771
3835
|
return 'tel:' + value;
|
3772
3836
|
} else {
|
3773
3837
|
// Check for URL scheme and default to http:// if none found
|
3774
|
-
return (urlSchemeRegex.test(value) ? '' : 'http://') + value;
|
3838
|
+
return (urlSchemeRegex.test(value) ? '' : 'http://') + encodeURI(value);
|
3775
3839
|
}
|
3776
3840
|
},
|
3777
3841
|
|
@@ -3988,6 +4052,15 @@ MediumEditor.extensions = {};
|
|
3988
4052
|
|
3989
4053
|
attachToEditables: function () {
|
3990
4054
|
this.subscribe('editableMouseover', this.handleEditableMouseover.bind(this));
|
4055
|
+
this.subscribe('positionedToolbar', this.handlePositionedToolbar.bind(this));
|
4056
|
+
},
|
4057
|
+
|
4058
|
+
handlePositionedToolbar: function () {
|
4059
|
+
// If the toolbar is visible and positioned, we don't need to hide the preview
|
4060
|
+
// when showWhenToolbarIsVisible is true
|
4061
|
+
if (!this.showWhenToolbarIsVisible) {
|
4062
|
+
this.hidePreview();
|
4063
|
+
}
|
3991
4064
|
},
|
3992
4065
|
|
3993
4066
|
handleClick: function (event) {
|
@@ -4537,8 +4610,12 @@ MediumEditor.extensions = {};
|
|
4537
4610
|
event.preventDefault();
|
4538
4611
|
event.stopPropagation();
|
4539
4612
|
|
4613
|
+
// command can be a function to execute
|
4614
|
+
if (typeof data.command === 'function') {
|
4615
|
+
data.command.apply(this);
|
4616
|
+
}
|
4540
4617
|
// command can be false so the shortcut is just disabled
|
4541
|
-
if (false !== data.command) {
|
4618
|
+
else if (false !== data.command) {
|
4542
4619
|
this.execAction(data.command);
|
4543
4620
|
}
|
4544
4621
|
}
|
@@ -4709,7 +4786,7 @@ MediumEditor.extensions = {};
|
|
4709
4786
|
if (font === '') {
|
4710
4787
|
this.clearFontName();
|
4711
4788
|
} else {
|
4712
|
-
this.execAction('fontName', {
|
4789
|
+
this.execAction('fontName', { value: font });
|
4713
4790
|
}
|
4714
4791
|
},
|
4715
4792
|
|
@@ -4887,7 +4964,7 @@ MediumEditor.extensions = {};
|
|
4887
4964
|
if (size === '4') {
|
4888
4965
|
this.clearFontSize();
|
4889
4966
|
} else {
|
4890
|
-
this.execAction('fontSize', {
|
4967
|
+
this.execAction('fontSize', { value: size });
|
4891
4968
|
}
|
4892
4969
|
},
|
4893
4970
|
|
@@ -4913,6 +4990,16 @@ MediumEditor.extensions = {};
|
|
4913
4990
|
}());
|
4914
4991
|
(function () {
|
4915
4992
|
'use strict';
|
4993
|
+
|
4994
|
+
/* Helpers and internal variables that don't need to be members of actual paste handler */
|
4995
|
+
|
4996
|
+
var pasteBinDefaultContent = '%ME_PASTEBIN%',
|
4997
|
+
lastRange = null,
|
4998
|
+
keyboardPasteEditable = null,
|
4999
|
+
stopProp = function (event) {
|
5000
|
+
event.stopPropagation();
|
5001
|
+
};
|
5002
|
+
|
4916
5003
|
/*jslint regexp: true*/
|
4917
5004
|
/*
|
4918
5005
|
jslint does not allow character negation, because the negation
|
@@ -4922,6 +5009,15 @@ MediumEditor.extensions = {};
|
|
4922
5009
|
*/
|
4923
5010
|
function createReplacements() {
|
4924
5011
|
return [
|
5012
|
+
// Remove anything but the contents within the BODY element
|
5013
|
+
[new RegExp(/^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g), ''],
|
5014
|
+
|
5015
|
+
// cleanup comments added by Chrome when pasting html
|
5016
|
+
[new RegExp(/<!--StartFragment-->|<!--EndFragment-->/g), ''],
|
5017
|
+
|
5018
|
+
// Trailing BR elements
|
5019
|
+
[new RegExp(/<br>$/i), ''],
|
5020
|
+
|
4925
5021
|
// replace two bogus tags that begin pastes from google docs
|
4926
5022
|
[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ''],
|
4927
5023
|
[new RegExp(/<\/b>(<br[^>]*>)?$/gi), ''],
|
@@ -4953,13 +5049,47 @@ MediumEditor.extensions = {};
|
|
4953
5049
|
// Microsoft Word makes these odd tags, like <o:p></o:p>
|
4954
5050
|
[new RegExp(/<\/?o:[a-z]*>/gi), ''],
|
4955
5051
|
|
4956
|
-
//
|
4957
|
-
[
|
4958
|
-
['<!--StartFragment-->', '']
|
5052
|
+
// Microsoft Word adds some special elements around list items
|
5053
|
+
[new RegExp(/<!\[if !supportLists\]>(((?!<!).)*)<!\[endif]\>/gi), '$1']
|
4959
5054
|
];
|
4960
5055
|
}
|
4961
5056
|
/*jslint regexp: false*/
|
4962
5057
|
|
5058
|
+
/**
|
5059
|
+
* Gets various content types out of the Clipboard API. It will also get the
|
5060
|
+
* plain text using older IE and WebKit API.
|
5061
|
+
*
|
5062
|
+
* @param {event} event Event fired on paste.
|
5063
|
+
* @param {win} reference to window
|
5064
|
+
* @param {doc} reference to document
|
5065
|
+
* @return {Object} Object with mime types and data for those mime types.
|
5066
|
+
*/
|
5067
|
+
function getClipboardContent(event, win, doc) {
|
5068
|
+
var dataTransfer = event.clipboardData || win.clipboardData || doc.dataTransfer,
|
5069
|
+
data = {};
|
5070
|
+
|
5071
|
+
if (!dataTransfer) {
|
5072
|
+
return data;
|
5073
|
+
}
|
5074
|
+
|
5075
|
+
// Use old WebKit/IE API
|
5076
|
+
if (dataTransfer.getData) {
|
5077
|
+
var legacyText = dataTransfer.getData('Text');
|
5078
|
+
if (legacyText && legacyText.length > 0) {
|
5079
|
+
data['text/plain'] = legacyText;
|
5080
|
+
}
|
5081
|
+
}
|
5082
|
+
|
5083
|
+
if (dataTransfer.types) {
|
5084
|
+
for (var i = 0; i < dataTransfer.types.length; i++) {
|
5085
|
+
var contentType = dataTransfer.types[i];
|
5086
|
+
data[contentType] = dataTransfer.getData(contentType);
|
5087
|
+
}
|
5088
|
+
}
|
5089
|
+
|
5090
|
+
return data;
|
5091
|
+
}
|
5092
|
+
|
4963
5093
|
var PasteHandler = MediumEditor.Extension.extend({
|
4964
5094
|
/* Paste Options */
|
4965
5095
|
|
@@ -5004,58 +5134,212 @@ MediumEditor.extensions = {};
|
|
5004
5134
|
|
5005
5135
|
if (this.forcePlainText || this.cleanPastedHTML) {
|
5006
5136
|
this.subscribe('editablePaste', this.handlePaste.bind(this));
|
5137
|
+
this.subscribe('editableKeydown', this.handleKeydown.bind(this));
|
5007
5138
|
}
|
5008
5139
|
},
|
5009
5140
|
|
5010
|
-
|
5011
|
-
|
5012
|
-
|
5013
|
-
|
5014
|
-
|
5015
|
-
|
5016
|
-
|
5017
|
-
|
5018
|
-
|
5019
|
-
|
5020
|
-
|
5141
|
+
destroy: function () {
|
5142
|
+
// Make sure pastebin is destroyed in case it's still around for some reason
|
5143
|
+
if (this.forcePlainText || this.cleanPastedHTML) {
|
5144
|
+
this.removePasteBin();
|
5145
|
+
}
|
5146
|
+
},
|
5147
|
+
|
5148
|
+
handlePaste: function (event, editable) {
|
5149
|
+
if (event.defaultPrevented) {
|
5150
|
+
return;
|
5151
|
+
}
|
5152
|
+
|
5153
|
+
var clipboardContent = getClipboardContent(event, this.window, this.document),
|
5154
|
+
pastedHTML = clipboardContent['text/html'],
|
5155
|
+
pastedPlain = clipboardContent['text/plain'];
|
5156
|
+
|
5157
|
+
if (this.window.clipboardData && event.clipboardData === undefined && !pastedHTML) {
|
5021
5158
|
// If window.clipboardData exists, but event.clipboardData doesn't exist,
|
5022
5159
|
// we're probably in IE. IE only has two possibilities for clipboard
|
5023
5160
|
// data format: 'Text' and 'URL'.
|
5024
5161
|
//
|
5025
|
-
//
|
5026
|
-
|
5027
|
-
dataFormatPlain = 'Text';
|
5162
|
+
// For IE, we'll fallback to 'Text' for text/html
|
5163
|
+
pastedHTML = pastedPlain;
|
5028
5164
|
}
|
5029
5165
|
|
5030
|
-
if (
|
5031
|
-
event.clipboardData.getData &&
|
5032
|
-
!event.defaultPrevented) {
|
5166
|
+
if (pastedHTML || pastedPlain) {
|
5033
5167
|
event.preventDefault();
|
5034
5168
|
|
5035
|
-
pastedHTML
|
5036
|
-
|
5169
|
+
this.doPaste(pastedHTML, pastedPlain, editable);
|
5170
|
+
}
|
5171
|
+
},
|
5037
5172
|
|
5038
|
-
|
5039
|
-
|
5040
|
-
|
5173
|
+
doPaste: function (pastedHTML, pastedPlain, editable) {
|
5174
|
+
var paragraphs,
|
5175
|
+
html = '',
|
5176
|
+
p;
|
5041
5177
|
|
5042
|
-
|
5043
|
-
|
5044
|
-
|
5045
|
-
|
5046
|
-
|
5047
|
-
|
5048
|
-
|
5049
|
-
|
5178
|
+
if (this.cleanPastedHTML && pastedHTML) {
|
5179
|
+
return this.cleanPaste(pastedHTML);
|
5180
|
+
}
|
5181
|
+
|
5182
|
+
if (!(this.getEditorOption('disableReturn') || (editable && editable.getAttribute('data-disable-return')))) {
|
5183
|
+
paragraphs = pastedPlain.split(/[\r\n]+/g);
|
5184
|
+
// If there are no \r\n in data, don't wrap in <p>
|
5185
|
+
if (paragraphs.length > 1) {
|
5186
|
+
for (p = 0; p < paragraphs.length; p += 1) {
|
5187
|
+
if (paragraphs[p] !== '') {
|
5188
|
+
html += '<p>' + MediumEditor.util.htmlEntities(paragraphs[p]) + '</p>';
|
5050
5189
|
}
|
5051
|
-
} else {
|
5052
|
-
html = MediumEditor.util.htmlEntities(paragraphs[0]);
|
5053
5190
|
}
|
5054
5191
|
} else {
|
5055
|
-
html = MediumEditor.util.htmlEntities(
|
5192
|
+
html = MediumEditor.util.htmlEntities(paragraphs[0]);
|
5193
|
+
}
|
5194
|
+
} else {
|
5195
|
+
html = MediumEditor.util.htmlEntities(pastedPlain);
|
5196
|
+
}
|
5197
|
+
MediumEditor.util.insertHTMLCommand(this.document, html);
|
5198
|
+
},
|
5199
|
+
|
5200
|
+
handlePasteBinPaste: function (event) {
|
5201
|
+
if (event.defaultPrevented) {
|
5202
|
+
this.removePasteBin();
|
5203
|
+
return;
|
5204
|
+
}
|
5205
|
+
|
5206
|
+
var clipboardContent = getClipboardContent(event, this.window, this.document),
|
5207
|
+
pastedHTML = clipboardContent['text/html'],
|
5208
|
+
pastedPlain = clipboardContent['text/plain'],
|
5209
|
+
editable = keyboardPasteEditable;
|
5210
|
+
|
5211
|
+
// If we have valid html already, or we're not in cleanPastedHTML mode
|
5212
|
+
// we can ignore the paste bin and just paste now
|
5213
|
+
if (!this.cleanPastedHTML || pastedHTML) {
|
5214
|
+
event.preventDefault();
|
5215
|
+
this.removePasteBin();
|
5216
|
+
this.doPaste(pastedHTML, pastedPlain, editable);
|
5217
|
+
return;
|
5218
|
+
}
|
5219
|
+
|
5220
|
+
// We need to look at the paste bin, so do a setTimeout to let the paste
|
5221
|
+
// fall through into the paste bin
|
5222
|
+
setTimeout(function () {
|
5223
|
+
// Only look for HTML if we're in cleanPastedHTML mode
|
5224
|
+
if (this.cleanPastedHTML) {
|
5225
|
+
// If clipboard didn't have HTML, try the paste bin
|
5226
|
+
pastedHTML = this.getPasteBinHtml();
|
5227
|
+
}
|
5228
|
+
|
5229
|
+
// If we needed the paste bin, we're done with it now, remove it
|
5230
|
+
this.removePasteBin();
|
5231
|
+
|
5232
|
+
// Handle the paste with the html from the paste bin
|
5233
|
+
this.doPaste(pastedHTML, pastedPlain, editable);
|
5234
|
+
}.bind(this), 0);
|
5235
|
+
},
|
5236
|
+
|
5237
|
+
handleKeydown: function (event, editable) {
|
5238
|
+
// if it's not Ctrl+V, do nothing
|
5239
|
+
if (!(MediumEditor.util.isKey(event, MediumEditor.util.keyCode.V) && MediumEditor.util.isMetaCtrlKey(event))) {
|
5240
|
+
return;
|
5241
|
+
}
|
5242
|
+
|
5243
|
+
event.stopImmediatePropagation();
|
5244
|
+
|
5245
|
+
this.removePasteBin();
|
5246
|
+
this.createPasteBin(editable);
|
5247
|
+
},
|
5248
|
+
|
5249
|
+
createPasteBin: function (editable) {
|
5250
|
+
var rects,
|
5251
|
+
range = MediumEditor.selection.getSelectionRange(this.document),
|
5252
|
+
top = this.window.pageYOffset;
|
5253
|
+
|
5254
|
+
keyboardPasteEditable = editable;
|
5255
|
+
|
5256
|
+
if (range) {
|
5257
|
+
rects = range.getClientRects();
|
5258
|
+
|
5259
|
+
// on empty line, rects is empty so we grab information from the first container of the range
|
5260
|
+
if (rects.length) {
|
5261
|
+
top += rects[0].top;
|
5262
|
+
} else {
|
5263
|
+
top += range.startContainer.getBoundingClientRect().top;
|
5056
5264
|
}
|
5057
|
-
MediumEditor.util.insertHTMLCommand(this.document, html);
|
5058
5265
|
}
|
5266
|
+
|
5267
|
+
lastRange = range;
|
5268
|
+
|
5269
|
+
var pasteBinElm = this.document.createElement('div');
|
5270
|
+
pasteBinElm.id = this.pasteBinId = 'medium-editor-pastebin-' + (+Date.now());
|
5271
|
+
pasteBinElm.setAttribute('style', 'border: 1px red solid; position: absolute; top: ' + top + 'px; width: 10px; height: 10px; overflow: hidden; opacity: 0');
|
5272
|
+
pasteBinElm.setAttribute('contentEditable', true);
|
5273
|
+
pasteBinElm.innerHTML = pasteBinDefaultContent;
|
5274
|
+
|
5275
|
+
this.document.body.appendChild(pasteBinElm);
|
5276
|
+
|
5277
|
+
// avoid .focus() to stop other event (actually the paste event)
|
5278
|
+
this.on(pasteBinElm, 'focus', stopProp);
|
5279
|
+
this.on(pasteBinElm, 'focusin', stopProp);
|
5280
|
+
this.on(pasteBinElm, 'focusout', stopProp);
|
5281
|
+
|
5282
|
+
pasteBinElm.focus();
|
5283
|
+
|
5284
|
+
MediumEditor.selection.selectNode(pasteBinElm, this.document);
|
5285
|
+
|
5286
|
+
if (!this.boundHandlePaste) {
|
5287
|
+
this.boundHandlePaste = this.handlePasteBinPaste.bind(this);
|
5288
|
+
}
|
5289
|
+
|
5290
|
+
this.on(pasteBinElm, 'paste', this.boundHandlePaste);
|
5291
|
+
},
|
5292
|
+
|
5293
|
+
removePasteBin: function () {
|
5294
|
+
if (null !== lastRange) {
|
5295
|
+
MediumEditor.selection.selectRange(this.document, lastRange);
|
5296
|
+
lastRange = null;
|
5297
|
+
}
|
5298
|
+
|
5299
|
+
if (null !== keyboardPasteEditable) {
|
5300
|
+
keyboardPasteEditable = null;
|
5301
|
+
}
|
5302
|
+
|
5303
|
+
var pasteBinElm = this.getPasteBin();
|
5304
|
+
if (!pasteBinElm) {
|
5305
|
+
return;
|
5306
|
+
}
|
5307
|
+
|
5308
|
+
if (pasteBinElm) {
|
5309
|
+
this.off(pasteBinElm, 'focus', stopProp);
|
5310
|
+
this.off(pasteBinElm, 'focusin', stopProp);
|
5311
|
+
this.off(pasteBinElm, 'focusout', stopProp);
|
5312
|
+
this.off(pasteBinElm, 'paste', this.boundHandlePaste);
|
5313
|
+
pasteBinElm.parentElement.removeChild(pasteBinElm);
|
5314
|
+
}
|
5315
|
+
},
|
5316
|
+
|
5317
|
+
getPasteBin: function () {
|
5318
|
+
return this.document.getElementById(this.pasteBinId);
|
5319
|
+
},
|
5320
|
+
|
5321
|
+
getPasteBinHtml: function () {
|
5322
|
+
var pasteBinElm = this.getPasteBin();
|
5323
|
+
|
5324
|
+
if (!pasteBinElm) {
|
5325
|
+
return false;
|
5326
|
+
}
|
5327
|
+
|
5328
|
+
// WebKit has a nice bug where it clones the paste bin if you paste from for example notepad
|
5329
|
+
// so we need to force plain text mode in this case
|
5330
|
+
if (pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') {
|
5331
|
+
return false;
|
5332
|
+
}
|
5333
|
+
|
5334
|
+
var pasteBinHtml = pasteBinElm.innerHTML;
|
5335
|
+
|
5336
|
+
// If paste bin is empty try using plain text mode
|
5337
|
+
// since that is better than nothing right
|
5338
|
+
if (!pasteBinHtml || pasteBinHtml === pasteBinDefaultContent) {
|
5339
|
+
return false;
|
5340
|
+
}
|
5341
|
+
|
5342
|
+
return pasteBinHtml;
|
5059
5343
|
},
|
5060
5344
|
|
5061
5345
|
cleanPaste: function (text) {
|
@@ -5134,16 +5418,19 @@ MediumEditor.extensions = {};
|
|
5134
5418
|
MediumEditor.util.insertHTMLCommand(this.document, fragmentBody.innerHTML.replace(/ /g, ' '));
|
5135
5419
|
},
|
5136
5420
|
|
5421
|
+
// TODO (6.0): Make this an internal helper instead of member of paste handler
|
5137
5422
|
isCommonBlock: function (el) {
|
5138
5423
|
return (el && (el.nodeName.toLowerCase() === 'p' || el.nodeName.toLowerCase() === 'div'));
|
5139
5424
|
},
|
5140
5425
|
|
5426
|
+
// TODO (6.0): Make this an internal helper instead of member of paste handler
|
5141
5427
|
filterCommonBlocks: function (el) {
|
5142
5428
|
if (/^\s*$/.test(el.textContent) && el.parentNode) {
|
5143
5429
|
el.parentNode.removeChild(el);
|
5144
5430
|
}
|
5145
5431
|
},
|
5146
5432
|
|
5433
|
+
// TODO (6.0): Make this an internal helper instead of member of paste handler
|
5147
5434
|
filterLineBreak: function (el) {
|
5148
5435
|
if (this.isCommonBlock(el.previousElementSibling)) {
|
5149
5436
|
// remove stray br's following common block elements
|
@@ -5157,6 +5444,7 @@ MediumEditor.extensions = {};
|
|
5157
5444
|
}
|
5158
5445
|
},
|
5159
5446
|
|
5447
|
+
// TODO (6.0): Make this an internal helper instead of member of paste handler
|
5160
5448
|
// remove an element, including its parent, if it is the only element within its parent
|
5161
5449
|
removeWithParent: function (el) {
|
5162
5450
|
if (el && el.parentNode) {
|
@@ -5168,6 +5456,7 @@ MediumEditor.extensions = {};
|
|
5168
5456
|
}
|
5169
5457
|
},
|
5170
5458
|
|
5459
|
+
// TODO (6.0): Make this an internal helper instead of member of paste handler
|
5171
5460
|
cleanupSpans: function (containerEl) {
|
5172
5461
|
var i,
|
5173
5462
|
el,
|
@@ -5825,30 +6114,26 @@ MediumEditor.extensions = {};
|
|
5825
6114
|
|
5826
6115
|
setToolbarPosition: function () {
|
5827
6116
|
var container = this.base.getFocusedElement(),
|
5828
|
-
selection = this.window.getSelection()
|
5829
|
-
anchorPreview;
|
6117
|
+
selection = this.window.getSelection();
|
5830
6118
|
|
5831
6119
|
// If there isn't a valid selection, bail
|
5832
6120
|
if (!container) {
|
5833
6121
|
return this;
|
5834
6122
|
}
|
5835
6123
|
|
5836
|
-
if (this.static
|
5837
|
-
this.showToolbar();
|
5838
|
-
this.positionStaticToolbar(container);
|
5839
|
-
} else if (!selection.isCollapsed) {
|
6124
|
+
if (this.static || !selection.isCollapsed) {
|
5840
6125
|
this.showToolbar();
|
5841
6126
|
|
5842
6127
|
// we don't need any absolute positioning if relativeContainer is set
|
5843
6128
|
if (!this.relativeContainer) {
|
5844
|
-
this.
|
6129
|
+
if (this.static) {
|
6130
|
+
this.positionStaticToolbar(container);
|
6131
|
+
} else {
|
6132
|
+
this.positionToolbar(selection);
|
6133
|
+
}
|
5845
6134
|
}
|
5846
|
-
}
|
5847
|
-
|
5848
|
-
anchorPreview = this.base.getExtensionByName('anchor-preview');
|
5849
6135
|
|
5850
|
-
|
5851
|
-
anchorPreview.hidePreview();
|
6136
|
+
this.trigger('positionedToolbar', {}, this.base.getFocusedElement());
|
5852
6137
|
}
|
5853
6138
|
},
|
5854
6139
|
|
@@ -6173,7 +6458,9 @@ MediumEditor.extensions = {};
|
|
6173
6458
|
return;
|
6174
6459
|
}
|
6175
6460
|
|
6176
|
-
|
6461
|
+
// https://github.com/yabwe/medium-editor/issues/994
|
6462
|
+
// Firefox thrown an error when calling `formatBlock` on an empty editable blockContainer that's not a <div>
|
6463
|
+
if (MediumEditor.util.isMediumEditorElement(node) && node.children.length === 0 && !MediumEditor.util.isBlockContainer(node)) {
|
6177
6464
|
this.options.ownerDocument.execCommand('formatBlock', false, 'p');
|
6178
6465
|
}
|
6179
6466
|
|
@@ -6194,6 +6481,13 @@ MediumEditor.extensions = {};
|
|
6194
6481
|
}
|
6195
6482
|
}
|
6196
6483
|
|
6484
|
+
function handleEditableInput(event, editable) {
|
6485
|
+
var textarea = editable.parentNode.querySelector('textarea[medium-editor-textarea-id="' + editable.getAttribute('medium-editor-textarea-id') + '"]');
|
6486
|
+
if (textarea) {
|
6487
|
+
textarea.value = editable.innerHTML.trim();
|
6488
|
+
}
|
6489
|
+
}
|
6490
|
+
|
6197
6491
|
// Internal helper methods which shouldn't be exposed externally
|
6198
6492
|
|
6199
6493
|
function addToEditors(win) {
|
@@ -6227,30 +6521,50 @@ MediumEditor.extensions = {};
|
|
6227
6521
|
win._mediumEditors[this.id] = null;
|
6228
6522
|
}
|
6229
6523
|
|
6230
|
-
function createElementsArray(selector) {
|
6524
|
+
function createElementsArray(selector, doc, filterEditorElements) {
|
6525
|
+
var elements = [];
|
6526
|
+
|
6231
6527
|
if (!selector) {
|
6232
6528
|
selector = [];
|
6233
6529
|
}
|
6234
6530
|
// If string, use as query selector
|
6235
6531
|
if (typeof selector === 'string') {
|
6236
|
-
selector =
|
6532
|
+
selector = doc.querySelectorAll(selector);
|
6237
6533
|
}
|
6238
6534
|
// If element, put into array
|
6239
6535
|
if (MediumEditor.util.isElement(selector)) {
|
6240
6536
|
selector = [selector];
|
6241
6537
|
}
|
6242
|
-
// Convert NodeList (or other array like object) into an array
|
6243
|
-
var elements = Array.prototype.slice.apply(selector);
|
6244
6538
|
|
6245
|
-
|
6246
|
-
|
6247
|
-
|
6248
|
-
|
6249
|
-
|
6250
|
-
|
6251
|
-
|
6539
|
+
if (filterEditorElements) {
|
6540
|
+
// Remove elements that have already been initialized by the editor
|
6541
|
+
// selecotr might not be an array (ie NodeList) so use for loop
|
6542
|
+
for (var i = 0; i < selector.length; i++) {
|
6543
|
+
var el = selector[i];
|
6544
|
+
if (MediumEditor.util.isElement(el) &&
|
6545
|
+
!el.getAttribute('data-medium-editor-element') &&
|
6546
|
+
!el.getAttribute('medium-editor-textarea-id')) {
|
6547
|
+
elements.push(el);
|
6548
|
+
}
|
6252
6549
|
}
|
6253
|
-
}
|
6550
|
+
} else {
|
6551
|
+
// Convert NodeList (or other array like object) into an array
|
6552
|
+
elements = Array.prototype.slice.apply(selector);
|
6553
|
+
}
|
6554
|
+
|
6555
|
+
return elements;
|
6556
|
+
}
|
6557
|
+
|
6558
|
+
function cleanupTextareaElement(element) {
|
6559
|
+
var textarea = element.parentNode.querySelector('textarea[medium-editor-textarea-id="' + element.getAttribute('medium-editor-textarea-id') + '"]');
|
6560
|
+
if (textarea) {
|
6561
|
+
// Un-hide the textarea
|
6562
|
+
textarea.classList.remove('medium-editor-hidden');
|
6563
|
+
textarea.removeAttribute('medium-editor-textarea-id');
|
6564
|
+
}
|
6565
|
+
if (element.parentNode) {
|
6566
|
+
element.parentNode.removeChild(element);
|
6567
|
+
}
|
6254
6568
|
}
|
6255
6569
|
|
6256
6570
|
function setExtensionDefaults(extension, defaults) {
|
@@ -6328,15 +6642,15 @@ MediumEditor.extensions = {};
|
|
6328
6642
|
return !this.options.extensions['imageDragging'];
|
6329
6643
|
}
|
6330
6644
|
|
6331
|
-
function createContentEditable(textarea, id) {
|
6332
|
-
var div =
|
6645
|
+
function createContentEditable(textarea, id, doc) {
|
6646
|
+
var div = doc.createElement('div'),
|
6333
6647
|
now = Date.now(),
|
6334
6648
|
uniqueId = 'medium-editor-' + now + '-' + id,
|
6335
6649
|
atts = textarea.attributes;
|
6336
6650
|
|
6337
6651
|
// Some browsers can move pretty fast, since we're using a timestamp
|
6338
6652
|
// to make a unique-id, ensure that the id is actually unique on the page
|
6339
|
-
while (
|
6653
|
+
while (doc.getElementById(uniqueId)) {
|
6340
6654
|
now++;
|
6341
6655
|
uniqueId = 'medium-editor-' + now + '-' + id;
|
6342
6656
|
}
|
@@ -6364,37 +6678,49 @@ MediumEditor.extensions = {};
|
|
6364
6678
|
return div;
|
6365
6679
|
}
|
6366
6680
|
|
6367
|
-
function
|
6368
|
-
|
6681
|
+
function initElement(element, id) {
|
6682
|
+
if (!element.getAttribute('data-medium-editor-element')) {
|
6683
|
+
if (element.nodeName.toLowerCase() === 'textarea') {
|
6684
|
+
element = createContentEditable(element, id, this.options.ownerDocument);
|
6685
|
+
|
6686
|
+
// Make sure we only attach to editableInput once for <textarea> elements
|
6687
|
+
if (!this.instanceHandleEditableInput) {
|
6688
|
+
this.instanceHandleEditableInput = handleEditableInput.bind(this);
|
6689
|
+
this.subscribe('editableInput', this.instanceHandleEditableInput);
|
6690
|
+
}
|
6691
|
+
}
|
6369
6692
|
|
6370
|
-
this.elements.forEach(function (element, index) {
|
6371
6693
|
if (!this.options.disableEditing && !element.getAttribute('data-disable-editing')) {
|
6372
6694
|
element.setAttribute('contentEditable', true);
|
6373
6695
|
element.setAttribute('spellcheck', this.options.spellcheck);
|
6374
6696
|
}
|
6697
|
+
|
6698
|
+
// Make sure we only attach to editableKeydownEnter once for disable-return options
|
6699
|
+
if (!this.instanceHandleEditableKeydownEnter) {
|
6700
|
+
if (element.getAttribute('data-disable-return') || element.getAttribute('data-disable-double-return')) {
|
6701
|
+
this.instanceHandleEditableKeydownEnter = handleDisabledEnterKeydown.bind(this);
|
6702
|
+
this.subscribe('editableKeydownEnter', this.instanceHandleEditableKeydownEnter);
|
6703
|
+
}
|
6704
|
+
}
|
6705
|
+
|
6706
|
+
// if we're not disabling return, add a handler to help handle cleanup
|
6707
|
+
// for certain cases when enter is pressed
|
6708
|
+
if (!this.options.disableReturn && !element.getAttribute('data-disable-return')) {
|
6709
|
+
this.on(element, 'keyup', handleKeyup.bind(this));
|
6710
|
+
}
|
6711
|
+
|
6375
6712
|
element.setAttribute('data-medium-editor-element', true);
|
6376
6713
|
element.setAttribute('role', 'textbox');
|
6377
6714
|
element.setAttribute('aria-multiline', true);
|
6378
|
-
element.setAttribute('medium-editor-index',
|
6379
|
-
|
6380
|
-
if (element.hasAttribute('medium-editor-textarea-id')) {
|
6381
|
-
isTextareaUsed = true;
|
6382
|
-
}
|
6383
|
-
}, this);
|
6715
|
+
element.setAttribute('medium-editor-index', MediumEditor.util.guid());
|
6384
6716
|
|
6385
|
-
|
6386
|
-
this.subscribe('editableInput', function (event, editable) {
|
6387
|
-
var textarea = editable.parentNode.querySelector('textarea[medium-editor-textarea-id="' + editable.getAttribute('medium-editor-textarea-id') + '"]');
|
6388
|
-
if (textarea) {
|
6389
|
-
textarea.value = this.serialize()[editable.id].value;
|
6390
|
-
}
|
6391
|
-
}.bind(this));
|
6717
|
+
this.events.attachAllEventsToElement(element);
|
6392
6718
|
}
|
6719
|
+
|
6720
|
+
return element;
|
6393
6721
|
}
|
6394
6722
|
|
6395
6723
|
function attachHandlers() {
|
6396
|
-
var i;
|
6397
|
-
|
6398
6724
|
// attach to tabs
|
6399
6725
|
this.subscribe('editableKeydownTab', handleTabKeydown.bind(this));
|
6400
6726
|
|
@@ -6407,27 +6733,14 @@ MediumEditor.extensions = {};
|
|
6407
6733
|
this.subscribe('editableKeydownSpace', handleDisableExtraSpaces.bind(this));
|
6408
6734
|
}
|
6409
6735
|
|
6410
|
-
//
|
6411
|
-
if (this.
|
6412
|
-
|
6413
|
-
|
6414
|
-
|
6415
|
-
|
6416
|
-
this.subscribe('editableKeydownEnter', handleDisabledEnterKeydown.bind(this));
|
6417
|
-
break;
|
6418
|
-
}
|
6736
|
+
// Make sure we only attach to editableKeydownEnter once for disable-return options
|
6737
|
+
if (!this.instanceHandleEditableKeydownEnter) {
|
6738
|
+
// disabling return or double return
|
6739
|
+
if (this.options.disableReturn || this.options.disableDoubleReturn) {
|
6740
|
+
this.instanceHandleEditableKeydownEnter = handleDisabledEnterKeydown.bind(this);
|
6741
|
+
this.subscribe('editableKeydownEnter', this.instanceHandleEditableKeydownEnter);
|
6419
6742
|
}
|
6420
6743
|
}
|
6421
|
-
|
6422
|
-
// if we're not disabling return, add a handler to help handle cleanup
|
6423
|
-
// for certain cases when enter is pressed
|
6424
|
-
if (!this.options.disableReturn) {
|
6425
|
-
this.elements.forEach(function (element) {
|
6426
|
-
if (!element.getAttribute('data-disable-return')) {
|
6427
|
-
this.on(element, 'keyup', handleKeyup.bind(this));
|
6428
|
-
}
|
6429
|
-
}, this);
|
6430
|
-
}
|
6431
6744
|
}
|
6432
6745
|
|
6433
6746
|
function initExtensions() {
|
@@ -6519,7 +6832,8 @@ MediumEditor.extensions = {};
|
|
6519
6832
|
/*jslint regexp: true*/
|
6520
6833
|
var appendAction = /^append-(.+)$/gi,
|
6521
6834
|
justifyAction = /justify([A-Za-z]*)$/g, /* Detecting if is justifyCenter|Right|Left */
|
6522
|
-
match
|
6835
|
+
match,
|
6836
|
+
cmdValueArgument;
|
6523
6837
|
/*jslint regexp: false*/
|
6524
6838
|
|
6525
6839
|
// Actions starting with 'append-' should attempt to format a block of text ('formatBlock') using a specific
|
@@ -6530,11 +6844,21 @@ MediumEditor.extensions = {};
|
|
6530
6844
|
}
|
6531
6845
|
|
6532
6846
|
if (action === 'fontSize') {
|
6533
|
-
|
6847
|
+
// TODO: Deprecate support for opts.size in 6.0.0
|
6848
|
+
if (opts.size) {
|
6849
|
+
MediumEditor.util.deprecated('.size option for fontSize command', '.value', '6.0.0');
|
6850
|
+
}
|
6851
|
+
cmdValueArgument = opts.value || opts.size;
|
6852
|
+
return this.options.ownerDocument.execCommand('fontSize', false, cmdValueArgument);
|
6534
6853
|
}
|
6535
6854
|
|
6536
6855
|
if (action === 'fontName') {
|
6537
|
-
|
6856
|
+
// TODO: Deprecate support for opts.name in 6.0.0
|
6857
|
+
if (opts.name) {
|
6858
|
+
MediumEditor.util.deprecated('.name option for fontName command', '.value', '6.0.0');
|
6859
|
+
}
|
6860
|
+
cmdValueArgument = opts.value || opts.name;
|
6861
|
+
return this.options.ownerDocument.execCommand('fontName', false, cmdValueArgument);
|
6538
6862
|
}
|
6539
6863
|
|
6540
6864
|
if (action === 'createLink') {
|
@@ -6558,7 +6882,8 @@ MediumEditor.extensions = {};
|
|
6558
6882
|
return result;
|
6559
6883
|
}
|
6560
6884
|
|
6561
|
-
|
6885
|
+
cmdValueArgument = opts && opts.value;
|
6886
|
+
return this.options.ownerDocument.execCommand(action, false, cmdValueArgument);
|
6562
6887
|
}
|
6563
6888
|
|
6564
6889
|
/* If we've just justified text within a container block
|
@@ -6624,7 +6949,10 @@ MediumEditor.extensions = {};
|
|
6624
6949
|
return;
|
6625
6950
|
}
|
6626
6951
|
|
6627
|
-
|
6952
|
+
this.events = new MediumEditor.Events(this);
|
6953
|
+
this.elements = [];
|
6954
|
+
|
6955
|
+
this.addElements(this.origElements);
|
6628
6956
|
|
6629
6957
|
if (this.elements.length === 0) {
|
6630
6958
|
return;
|
@@ -6633,10 +6961,7 @@ MediumEditor.extensions = {};
|
|
6633
6961
|
this.isActive = true;
|
6634
6962
|
addToEditors.call(this, this.options.contentWindow);
|
6635
6963
|
|
6636
|
-
this.events = new MediumEditor.Events(this);
|
6637
|
-
|
6638
6964
|
// Call initialization helpers
|
6639
|
-
initElements.call(this);
|
6640
6965
|
initExtensions.call(this);
|
6641
6966
|
attachHandlers.call(this);
|
6642
6967
|
},
|
@@ -6671,40 +6996,45 @@ MediumEditor.extensions = {};
|
|
6671
6996
|
element.removeAttribute('medium-editor-index');
|
6672
6997
|
|
6673
6998
|
// Remove any elements created for textareas
|
6674
|
-
if (element.
|
6675
|
-
|
6676
|
-
if (textarea) {
|
6677
|
-
// Un-hide the textarea
|
6678
|
-
textarea.classList.remove('medium-editor-hidden');
|
6679
|
-
}
|
6680
|
-
if (element.parentNode) {
|
6681
|
-
element.parentNode.removeChild(element);
|
6682
|
-
}
|
6999
|
+
if (element.getAttribute('medium-editor-textarea-id')) {
|
7000
|
+
cleanupTextareaElement(element);
|
6683
7001
|
}
|
6684
7002
|
}, this);
|
6685
7003
|
this.elements = [];
|
7004
|
+
this.instanceHandleEditableKeydownEnter = null;
|
7005
|
+
this.instanceHandleEditableInput = null;
|
6686
7006
|
|
6687
7007
|
removeFromEditors.call(this, this.options.contentWindow);
|
6688
7008
|
},
|
6689
7009
|
|
6690
7010
|
on: function (target, event, listener, useCapture) {
|
6691
7011
|
this.events.attachDOMEvent(target, event, listener, useCapture);
|
7012
|
+
|
7013
|
+
return this;
|
6692
7014
|
},
|
6693
7015
|
|
6694
7016
|
off: function (target, event, listener, useCapture) {
|
6695
7017
|
this.events.detachDOMEvent(target, event, listener, useCapture);
|
7018
|
+
|
7019
|
+
return this;
|
6696
7020
|
},
|
6697
7021
|
|
6698
7022
|
subscribe: function (event, listener) {
|
6699
7023
|
this.events.attachCustomEvent(event, listener);
|
7024
|
+
|
7025
|
+
return this;
|
6700
7026
|
},
|
6701
7027
|
|
6702
7028
|
unsubscribe: function (event, listener) {
|
6703
7029
|
this.events.detachCustomEvent(event, listener);
|
7030
|
+
|
7031
|
+
return this;
|
6704
7032
|
},
|
6705
7033
|
|
6706
7034
|
trigger: function (name, data, editable) {
|
6707
7035
|
this.events.triggerCustomEvent(name, data, editable);
|
7036
|
+
|
7037
|
+
return this;
|
6708
7038
|
},
|
6709
7039
|
|
6710
7040
|
delay: function (fn) {
|
@@ -6719,8 +7049,10 @@ MediumEditor.extensions = {};
|
|
6719
7049
|
serialize: function () {
|
6720
7050
|
var i,
|
6721
7051
|
elementid,
|
6722
|
-
content = {}
|
6723
|
-
|
7052
|
+
content = {},
|
7053
|
+
len = this.elements.length;
|
7054
|
+
|
7055
|
+
for (i = 0; i < len; i += 1) {
|
6724
7056
|
elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
|
6725
7057
|
content[elementid] = {
|
6726
7058
|
value: this.elements[i].innerHTML.trim()
|
@@ -6954,7 +7286,8 @@ MediumEditor.extensions = {};
|
|
6954
7286
|
|
6955
7287
|
createLink: function (opts) {
|
6956
7288
|
var currentEditor = MediumEditor.selection.getSelectionElement(this.options.contentWindow),
|
6957
|
-
customEvent = {}
|
7289
|
+
customEvent = {},
|
7290
|
+
targetUrl;
|
6958
7291
|
|
6959
7292
|
// Make sure the selection is within an element this editor is tracking
|
6960
7293
|
if (this.elements.indexOf(currentEditor) === -1) {
|
@@ -6963,7 +7296,12 @@ MediumEditor.extensions = {};
|
|
6963
7296
|
|
6964
7297
|
try {
|
6965
7298
|
this.events.disableCustomEvent('editableInput');
|
6966
|
-
|
7299
|
+
// TODO: Deprecate support for opts.url in 6.0.0
|
7300
|
+
if (opts.url) {
|
7301
|
+
MediumEditor.util.deprecated('.url option for createLink', '.value', '6.0.0');
|
7302
|
+
}
|
7303
|
+
targetUrl = opts.url || opts.value;
|
7304
|
+
if (targetUrl && targetUrl.trim().length > 0) {
|
6967
7305
|
var currentSelection = this.options.contentWindow.getSelection();
|
6968
7306
|
if (currentSelection) {
|
6969
7307
|
var currRange = currentSelection.getRangeAt(0),
|
@@ -7055,7 +7393,7 @@ MediumEditor.extensions = {};
|
|
7055
7393
|
}
|
7056
7394
|
|
7057
7395
|
// Creates the link in the document fragment
|
7058
|
-
MediumEditor.util.createLink(this.options.ownerDocument, textNodes,
|
7396
|
+
MediumEditor.util.createLink(this.options.ownerDocument, textNodes, targetUrl.trim());
|
7059
7397
|
|
7060
7398
|
// Chrome trims the leading whitespaces when inserting HTML, which messes up restoring the selection.
|
7061
7399
|
var leadingWhitespacesCount = (fragment.firstChild.innerHTML.match(/^\s+/) || [''])[0].length;
|
@@ -7067,13 +7405,13 @@ MediumEditor.extensions = {};
|
|
7067
7405
|
|
7068
7406
|
this.importSelection(exportedSelection);
|
7069
7407
|
} else {
|
7070
|
-
this.options.ownerDocument.execCommand('createLink', false,
|
7408
|
+
this.options.ownerDocument.execCommand('createLink', false, targetUrl);
|
7071
7409
|
}
|
7072
7410
|
|
7073
7411
|
if (this.options.targetBlank || opts.target === '_blank') {
|
7074
|
-
MediumEditor.util.setTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument),
|
7412
|
+
MediumEditor.util.setTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
|
7075
7413
|
} else {
|
7076
|
-
MediumEditor.util.removeTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument),
|
7414
|
+
MediumEditor.util.removeTargetBlank(MediumEditor.selection.getSelectionStart(this.options.ownerDocument), targetUrl);
|
7077
7415
|
}
|
7078
7416
|
|
7079
7417
|
if (opts.buttonClass) {
|
@@ -7085,7 +7423,7 @@ MediumEditor.extensions = {};
|
|
7085
7423
|
if (this.options.targetBlank || opts.target === '_blank' || opts.buttonClass) {
|
7086
7424
|
customEvent = this.options.ownerDocument.createEvent('HTMLEvents');
|
7087
7425
|
customEvent.initEvent('input', true, true, this.options.contentWindow);
|
7088
|
-
for (var i = 0
|
7426
|
+
for (var i = 0, len = this.elements.length; i < len; i += 1) {
|
7089
7427
|
this.elements[i].dispatchEvent(customEvent);
|
7090
7428
|
}
|
7091
7429
|
}
|
@@ -7110,8 +7448,56 @@ MediumEditor.extensions = {};
|
|
7110
7448
|
if (this.elements[index]) {
|
7111
7449
|
var target = this.elements[index];
|
7112
7450
|
target.innerHTML = html;
|
7113
|
-
this.
|
7451
|
+
this.checkContentChanged(target);
|
7452
|
+
}
|
7453
|
+
},
|
7454
|
+
|
7455
|
+
checkContentChanged: function (editable) {
|
7456
|
+
editable = editable || MediumEditor.selection.getSelectionElement(this.options.contentWindow);
|
7457
|
+
this.events.updateInput(editable, { target: editable, currentTarget: editable });
|
7458
|
+
},
|
7459
|
+
|
7460
|
+
addElements: function (selector) {
|
7461
|
+
// Convert elements into an array
|
7462
|
+
var elements = createElementsArray(selector, this.options.ownerDocument, true);
|
7463
|
+
|
7464
|
+
// Do we have elements to add now?
|
7465
|
+
if (elements.length === 0) {
|
7466
|
+
return false;
|
7114
7467
|
}
|
7468
|
+
|
7469
|
+
elements.forEach(function (element) {
|
7470
|
+
// Initialize all new elements (we check that in those functions don't worry)
|
7471
|
+
element = initElement.call(this, element);
|
7472
|
+
|
7473
|
+
// Add new elements to our internal elements array
|
7474
|
+
this.elements.push(element);
|
7475
|
+
}, this);
|
7476
|
+
},
|
7477
|
+
|
7478
|
+
removeElements: function (selector) {
|
7479
|
+
// Convert elements into an array
|
7480
|
+
var elements = createElementsArray(selector, this.options.ownerDocument),
|
7481
|
+
toRemove = elements.map(function (el) {
|
7482
|
+
// For textareas, make sure we're looking at the editor div and not the textarea itself
|
7483
|
+
if (el.getAttribute('medium-editor-textarea-id') && el.parentNode) {
|
7484
|
+
return el.parentNode.querySelector('div[medium-editor-textarea-id="' + el.getAttribute('medium-editor-textarea-id') + '"]');
|
7485
|
+
} else {
|
7486
|
+
return el;
|
7487
|
+
}
|
7488
|
+
});
|
7489
|
+
|
7490
|
+
this.elements = this.elements.filter(function (element) {
|
7491
|
+
// If this is an element we want to remove
|
7492
|
+
if (toRemove.indexOf(element) !== -1) {
|
7493
|
+
this.events.cleanupElement(element);
|
7494
|
+
if (element.getAttribute('medium-editor-textarea-id')) {
|
7495
|
+
cleanupTextareaElement(element);
|
7496
|
+
}
|
7497
|
+
return false;
|
7498
|
+
}
|
7499
|
+
return true;
|
7500
|
+
}, this);
|
7115
7501
|
}
|
7116
7502
|
};
|
7117
7503
|
}());
|
@@ -7154,7 +7540,7 @@ MediumEditor.parseVersionString = function (release) {
|
|
7154
7540
|
|
7155
7541
|
MediumEditor.version = MediumEditor.parseVersionString.call(this, ({
|
7156
7542
|
// grunt-bump looks for this:
|
7157
|
-
'version': '5.
|
7543
|
+
'version': '5.18.0'
|
7158
7544
|
}).version);
|
7159
7545
|
|
7160
7546
|
return MediumEditor;
|
@@ -206,14 +206,15 @@
|
|
206
206
|
content: "";
|
207
207
|
display: table; }
|
208
208
|
|
209
|
-
[data-medium-editor-element]
|
210
|
-
|
211
|
-
|
212
|
-
[data-medium-editor-element]
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
209
|
+
[data-medium-editor-element] {
|
210
|
+
word-wrap: break-word;
|
211
|
+
min-height: 30px; }
|
212
|
+
[data-medium-editor-element] img {
|
213
|
+
max-width: 100%; }
|
214
|
+
[data-medium-editor-element] sub {
|
215
|
+
vertical-align: sub; }
|
216
|
+
[data-medium-editor-element] sup {
|
217
|
+
vertical-align: super; }
|
217
218
|
|
218
219
|
.medium-editor-hidden {
|
219
220
|
display: none; }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scrivito_editors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scrivito
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jquery-ui-rails
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.
|
47
|
+
version: 1.4.0.rc1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.
|
54
|
+
version: 1.4.0.rc1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: scrivito_sdk
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.4.0.rc1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
68
|
+
version: 1.4.0.rc1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: coffee-rails
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|