1
1
/**
2
- * @license wysihtml v0.5.0-beta14
2
+ * @license wysihtml v0.5.0
3
3
* https://github.com/Voog/wysihtml
4
4
*
5
5
* Author: Christopher Blum (https://github.com/tiff)
10
10
*
11
11
*/
12
12
var wysihtml5 = {
13
- version: "0.5.0-beta14 ",
13
+ version: "0.5.0",
14
14
15
15
// namespaces
16
16
commands: {},
@@ -7040,7 +7040,7 @@ wysihtml5.browser = (function() {
7040
7040
Should actually check for clipboardData on paste event, but cannot in firefox
7041
7041
*/
7042
7042
supportsModernPaste: function () {
7043
- return !("clipboardData" in window );
7043
+ return !isIE( );
7044
7044
},
7045
7045
7046
7046
// Unifies the property names of element.style by returning the suitable property name for current browser
@@ -9140,27 +9140,31 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
9140
9140
}
9141
9141
9142
9142
9143
- if (typeof(allowedClasses) === "string" && allowedClasses === "any" && oldNode.getAttribute("class")) {
9144
- if (currentRules.classes_blacklist) {
9145
- oldClasses = oldNode.getAttribute("class");
9146
- if (oldClasses) {
9147
- classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
9148
- }
9143
+ if (typeof(allowedClasses) === "string" && allowedClasses === "any") {
9144
+ if (oldNode.getAttribute("class")) {
9145
+ if (currentRules.classes_blacklist) {
9146
+ oldClasses = oldNode.getAttribute("class");
9147
+ if (oldClasses) {
9148
+ classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
9149
+ }
9149
9150
9150
- classesLength = classes.length;
9151
- for (; i<classesLength; i++) {
9152
- currentClass = classes[i];
9153
- if (!currentRules.classes_blacklist[currentClass]) {
9154
- newClasses.push(currentClass);
9151
+ classesLength = classes.length;
9152
+ for (; i<classesLength; i++) {
9153
+ currentClass = classes[i];
9154
+ if (!currentRules.classes_blacklist[currentClass]) {
9155
+ newClasses.push(currentClass);
9156
+ }
9155
9157
}
9156
- }
9157
9158
9158
- if (newClasses.length) {
9159
- attributes["class"] = wysihtml5.lang.array(newClasses).unique().join(" ");
9160
- }
9159
+ if (newClasses.length) {
9160
+ attributes["class"] = wysihtml5.lang.array(newClasses).unique().join(" ");
9161
+ }
9161
9162
9163
+ } else {
9164
+ attributes["class"] = oldNode.getAttribute("class");
9165
+ }
9162
9166
} else {
9163
- attributes["class"] = oldNode.getAttribute("class ");
9167
+ attributes["class"] = wysihtml5.lang.array(classes).unique().join(" ");
9164
9168
}
9165
9169
} else {
9166
9170
// make sure that wysihtml5 temp class doesn't get stripped out
@@ -10015,7 +10019,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
10015
10019
set = function() {
10016
10020
if (view.isEmpty() && !view.placeholderSet) {
10017
10021
view.placeholderSet = true;
10018
- view.setValue(placeholderText);
10022
+ view.setValue(placeholderText, false );
10019
10023
dom.addClass(view.element, CLASS_NAME);
10020
10024
}
10021
10025
};
@@ -11120,7 +11124,7 @@ wysihtml5.dom.unwrap = function(node) {
11120
11124
**/
11121
11125
wysihtml5.dom.getPastedHtml = function(event) {
11122
11126
var html;
11123
- if (event.clipboardData) {
11127
+ if (wysihtml5.browser.supportsModernPaste() && event.clipboardData) {
11124
11128
if (wysihtml5.lang.array(event.clipboardData.types).contains('text/html')) {
11125
11129
html = event.clipboardData.getData('text/html');
11126
11130
} else if (wysihtml5.lang.array(event.clipboardData.types).contains('text/plain')) {
@@ -11990,9 +11994,15 @@ wysihtml5.quirks.ensureProperClearing = (function() {
11990
11994
// Deletes selection contents making sure uneditables/unselectables are not partially deleted
11991
11995
// Triggers wysihtml5:uneditable:delete custom event on all deleted uneditables if customevents suppoorted
11992
11996
deleteContents: function() {
11993
- var range = this.getRange(),
11994
- startParent, endParent, uneditables, ev;
11995
-
11997
+ var range = this.getRange();
11998
+ this.deleteRangeContents(range);
11999
+ this.setSelection(range);
12000
+ },
12001
+
12002
+ // Makes sure all uneditable sare notified before deleting contents
12003
+ deleteRangeContents: function (range) {
12004
+ var startParent, endParent, uneditables, ev;
12005
+
11996
12006
if (this.unselectableClass) {
11997
12007
if ((startParent = wysihtml5.dom.getParentElement(range.startContainer, { query: "." + this.unselectableClass }, false, this.contain))) {
11998
12008
range.setStartBefore(startParent);
@@ -12011,10 +12021,8 @@ wysihtml5.quirks.ensureProperClearing = (function() {
12011
12021
uneditables[i].dispatchEvent(ev);
12012
12022
} catch (err) {}
12013
12023
}
12014
-
12015
12024
}
12016
12025
range.deleteContents();
12017
- this.setSelection(range);
12018
12026
},
12019
12027
12020
12028
getPreviousNode: function(node, ignoreEmpty) {
@@ -12301,28 +12309,41 @@ wysihtml5.quirks.ensureProperClearing = (function() {
12301
12309
},
12302
12310
12303
12311
/**
12304
- * Insert html at the caret position and move the cursor after the inserted html
12312
+ * Insert html at the caret or selection position and move the cursor after the inserted html
12313
+ * Replaces selection content if present
12305
12314
*
12306
12315
* @param {String} html HTML string to insert
12307
12316
* @example
12308
12317
* selection.insertHTML("<p>foobar</p>");
12309
12318
*/
12310
12319
insertHTML: function(html) {
12311
- var range = rangy.createRange( this.doc ),
12320
+ var range = this.getRange( ),
12312
12321
node = this.doc.createElement('DIV'),
12313
12322
fragment = this.doc.createDocumentFragment(),
12314
- lastChild;
12315
-
12316
- node.innerHTML = html;
12317
- lastChild = node.lastChild;
12323
+ lastChild, lastEditorElement;
12324
+
12325
+ if (range) {
12326
+ range.deleteContents();
12327
+ node.innerHTML = html;
12328
+ lastChild = node.lastChild;
12318
12329
12319
- while (node.firstChild) {
12320
- fragment.appendChild(node.firstChild);
12321
- }
12322
- this.insertNode(fragment);
12330
+ while (node.firstChild) {
12331
+ fragment.appendChild(node.firstChild);
12332
+ }
12333
+ range.insertNode(fragment);
12334
+
12335
+ lastEditorElement = this.contain.lastChild;
12336
+ while (lastEditorElement && lastEditorElement.nodeType === 3 && lastEditorElement.previousSibling && (/^\s*$/).test(lastEditorElement.data)) {
12337
+ lastEditorElement = lastEditorElement.previousSibling;
12338
+ }
12323
12339
12324
- if (lastChild) {
12325
- this.setAfter(lastChild);
12340
+ if (lastChild) {
12341
+ // fixes some pad cases mostly on webkit where last nr is needed
12342
+ if (lastEditorElement && lastChild === lastEditorElement && lastChild.nodeType === 1) {
12343
+ this.contain.appendChild(this.doc.createElement('br'));
12344
+ }
12345
+ this.setAfter(lastChild);
12346
+ }
12326
12347
}
12327
12348
},
12328
12349
@@ -15100,11 +15121,7 @@ wysihtml5.Commands = Base.extend(
15100
15121
;(function(wysihtml5){
15101
15122
wysihtml5.commands.insertHTML = {
15102
15123
exec: function(composer, command, html) {
15103
- if (composer.commands.support(command)) {
15104
- composer.doc.execCommand(command, false, html);
15105
- } else {
15106
15124
composer.selection.insertHTML(html);
15107
- }
15108
15125
},
15109
15126
15110
15127
state: function() {
@@ -15226,14 +15243,7 @@ wysihtml5.Commands = Base.extend(
15226
15243
15227
15244
wysihtml5.commands.insertLineBreak = {
15228
15245
exec: function(composer, command) {
15229
- if (composer.commands.support(command)) {
15230
- composer.doc.execCommand(command, false, null);
15231
- if (!wysihtml5.browser.autoScrollsToCaret()) {
15232
- composer.selection.scrollIntoView();
15233
- }
15234
- } else {
15235
- composer.commands.exec("insertHTML", LINE_BREAK);
15236
- }
15246
+ composer.selection.insertHTML(LINE_BREAK);
15237
15247
},
15238
15248
15239
15249
state: function() {
@@ -16195,9 +16205,6 @@ wysihtml5.views.View = Base.extend(
16195
16205
/** @scope wysihtml5.views.Composer.prototype */ {
16196
16206
name: "composer",
16197
16207
16198
- // Needed for firefox in order to display a proper caret in an empty contentEditable
16199
- CARET_HACK: "<br>",
16200
-
16201
16208
constructor: function(parent, editableElement, config) {
16202
16209
this.base(parent, editableElement, config);
16203
16210
if (!this.config.noTextarea) {
@@ -16213,20 +16220,19 @@ wysihtml5.views.View = Base.extend(
16213
16220
},
16214
16221
16215
16222
clear: function() {
16216
- this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : this.CARET_HACK ;
16223
+ this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : "<br>" ;
16217
16224
},
16218
16225
16219
16226
getValue: function(parse, clearInternals) {
16220
16227
var value = this.isEmpty() ? "" : wysihtml5.quirks.getCorrectInnerHTML(this.element);
16221
16228
if (parse !== false) {
16222
16229
value = this.parent.parse(value, (clearInternals === false) ? false : true);
16223
16230
}
16224
-
16225
16231
return value;
16226
16232
},
16227
16233
16228
16234
setValue: function(html, parse) {
16229
- if (parse) {
16235
+ if (parse !== false ) {
16230
16236
html = this.parent.parse(html);
16231
16237
}
16232
16238
@@ -16237,12 +16243,12 @@ wysihtml5.views.View = Base.extend(
16237
16243
}
16238
16244
},
16239
16245
16240
- cleanUp: function() {
16246
+ cleanUp: function(rules ) {
16241
16247
var bookmark;
16242
16248
if (this.selection) {
16243
16249
bookmark = rangy.saveSelection(this.win);
16244
16250
}
16245
- this.parent.parse(this.element);
16251
+ this.parent.parse(this.element, undefined, rules );
16246
16252
if (bookmark) {
16247
16253
rangy.restoreSelection(bookmark);
16248
16254
}
@@ -16630,17 +16636,6 @@ wysihtml5.views.View = Base.extend(
16630
16636
});
16631
16637
}
16632
16638
16633
- // Under certain circumstances Chrome + Safari create nested <p> or <hX> tags after paste
16634
- // Inserting an invisible white space in front of it fixes the issue
16635
- // This is too hacky and causes selection not to replace content on paste in chrome
16636
- /* if (browser.createsNestedInvalidMarkupAfterPaste()) {
16637
- dom.observe(this.element, "paste", function(event) {
16638
- var invisibleSpace = that.doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
16639
- that.selection.insertNode(invisibleSpace);
16640
- });
16641
- }*/
16642
-
16643
-
16644
16639
dom.observe(this.element, "keydown", function(event) {
16645
16640
var keyCode = event.keyCode;
16646
16641
@@ -17087,7 +17082,7 @@ wysihtml5.views.View = Base.extend(
17087
17082
if (this.config.copyedFromMarking) {
17088
17083
// If supported the copied source can be based directly on selection
17089
17084
// Very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection.
17090
- if (event.clipboardData ) {
17085
+ if (wysihtml5.browser.supportsModernPaste() ) {
17091
17086
event.clipboardData.setData("text/html", this.config.copyedFromMarking + this.selection.getHtml());
17092
17087
event.clipboardData.setData("text/plain", this.selection.getPlainText());
17093
17088
event.preventDefault();
@@ -17466,14 +17461,14 @@ wysihtml5.views.View = Base.extend(
17466
17461
},
17467
17462
17468
17463
setValue: function(html, parse) {
17469
- if (parse) {
17464
+ if (parse !== false ) {
17470
17465
html = this.parent.parse(html);
17471
17466
}
17472
17467
this.element.value = html;
17473
17468
},
17474
17469
17475
- cleanUp: function() {
17476
- var html = this.parent.parse(this.element.value);
17470
+ cleanUp: function(rules ) {
17471
+ var html = this.parent.parse(this.element.value, undefined, rules );
17477
17472
this.element.value = html;
17478
17473
},
17479
17474
@@ -17568,7 +17563,7 @@ wysihtml5.views.View = Base.extend(
17568
17563
handleTabKey: true,
17569
17564
// Object which includes parser rules to apply when html gets cleaned
17570
17565
// See parser_rules/*.js for examples
17571
- parserRules: { tags: { br: {}, span: {}, div: {}, p: {} }, classes: {} },
17566
+ parserRules: { tags: { br: {}, span: {}, div: {}, p: {}, b: {}, i: {}, u: {} }, classes: {} },
17572
17567
// Object which includes parser when the user inserts content via copy & paste. If null parserRules will be used instead
17573
17568
pasteParserRulesets: null,
17574
17569
// Parser method to use when the user inserts content
@@ -17680,8 +17675,8 @@ wysihtml5.views.View = Base.extend(
17680
17675
return this;
17681
17676
},
17682
17677
17683
- cleanUp: function() {
17684
- this.currentView.cleanUp();
17678
+ cleanUp: function(rules ) {
17679
+ this.currentView.cleanUp(rules );
17685
17680
},
17686
17681
17687
17682
focus: function(setToEnd) {
@@ -17723,10 +17718,10 @@ wysihtml5.views.View = Base.extend(
17723
17718
this.off();
17724
17719
},
17725
17720
17726
- parse: function(htmlOrElement, clearInternals) {
17721
+ parse: function(htmlOrElement, clearInternals, customRules ) {
17727
17722
var parseContext = (this.config.contentEditableMode) ? document : ((this.composer) ? this.composer.sandbox.getDocument() : null);
17728
17723
var returnValue = this.config.parser(htmlOrElement, {
17729
- "rules": this.config.parserRules,
17724
+ "rules": customRules || this.config.parserRules,
17730
17725
"cleanUp": this.config.cleanUp,
17731
17726
"context": parseContext,
17732
17727
"uneditableClass": this.config.classNames.uneditableContainer,
0 commit comments