scrivito_editors 1.3.1 → 1.4.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|