wysihtml-rails 0.6.0.beta → 0.6.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f097e379e51c9e5cedc76fb7cef629cb029d802
4
- data.tar.gz: bf18f665794ac4be917f3863a77efc6d6c57fdec
3
+ metadata.gz: 28edad758722675d4832f0c387cb4b89b84697f1
4
+ data.tar.gz: 4bb7c150b90be7e92db95b36093ce4bbef02f3f5
5
5
  SHA512:
6
- metadata.gz: 3400cbead628315c726b280cb8c3301525dc6ad76f31393ce22811229d5db2d3b60c6980233784972a55cedbdd4bf0500bac729c936d6b54ee1442ee48b49019
7
- data.tar.gz: d9ff10cc2079e230640a79d33b22d62df11cacb6fc3adf98e63dce8452491803099209814dfc2f2a2c2ff798bdf45dd6e46fbbdc1f63255fe179d88e440c6684
6
+ metadata.gz: 554f22de0d93cdc3f3fdee7fa6c237a4d24dd147a5b50c8cf7b41bb384ab54832cff79fdb2c8ddafe900bff6b29bc811bb006a6c3739e488ea6d37426e76d4b9
7
+ data.tar.gz: 3bd893e4fad07835b6cba778913dbe408f3e4859aa0b62e927b850f343c885696e67285e9f44462c0ab60dcc380310f2521b64d419be5ba67d8cb8efc5bde949
@@ -1,5 +1,5 @@
1
1
  module Wysihtml
2
2
  module Rails
3
- VERSION = "0.6.0.beta"
3
+ VERSION = "0.6.0.beta2"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license wysihtml v0.6.0-beta
2
+ * @license wysihtml v0.6.0-beta2
3
3
  * https://github.com/Voog/wysihtml
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
@@ -10,7 +10,7 @@
10
10
  *
11
11
  */
12
12
  var wysihtml = {
13
- version: "0.6.0-beta",
13
+ version: '0.6.0-beta1',
14
14
 
15
15
  // namespaces
16
16
  commands: {},
@@ -26,10 +26,11 @@ var wysihtml = {
26
26
  this.editorExtenders.push(extender);
27
27
  },
28
28
 
29
- INVISIBLE_SPACE: "\uFEFF",
29
+ INVISIBLE_SPACE: '\uFEFF',
30
30
  INVISIBLE_SPACE_REG_EXP: /\uFEFF/g,
31
31
 
32
- VOID_ELEMENTS: "area, base, br, col, embed, hr, img, input, keygen, link, meta, param, source, track, wbr",
32
+ VOID_ELEMENTS: 'area, base, br, col, embed, hr, img, input, keygen, link, meta, param, source, track, wbr',
33
+ PERMITTED_PHRASING_CONTENT_ONLY: 'h1, h2, h3, h4, h5, h6, p, pre',
33
34
 
34
35
  EMPTY_FUNCTION: function() {},
35
36
 
@@ -46,260 +47,308 @@ var wysihtml = {
46
47
 
47
48
  wysihtml.polyfills = function(win, doc) {
48
49
 
49
- // TODO: in future try to replace most inline compability checks with polyfills for code readability
50
+ var methods = {
51
+
52
+ // Safary has a bug of not restoring selection after node.normalize correctly.
53
+ // Detects the misbegaviour and patches it
54
+ normalizeHasCaretError: function() {
55
+ if ("createRange" in doc && "getSelection" in win) {
56
+ var originalTarget,
57
+ scrollTop = window.pageYOffset,
58
+ scrollLeft = window.pageXOffset,
59
+ e = doc.createElement('div'),
60
+ t1 = doc.createTextNode('a'),
61
+ t2 = doc.createTextNode('a'),
62
+ t3 = doc.createTextNode('a'),
63
+ r = doc.createRange(),
64
+ s, ret;
65
+
66
+ if (document.activeElement) {
67
+ if (document.activeElement.nodeType === 1 && ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].indexOf(document.activeElement.nodeName) > -1) {
68
+ originalTarget = {
69
+ type: 'form',
70
+ node: document.activeElement,
71
+ start: document.activeElement.selectionStart,
72
+ end: document.activeElement.selectionEnd,
73
+ };
74
+ } else {
75
+ s = win.getSelection();
76
+ if (s && s.anchorNode) {
77
+ originalTarget = {
78
+ type: 'range',
79
+ anchorNode: s.anchorNode,
80
+ anchorOffset: s.anchorOffset,
81
+ focusNode: s.focusNode,
82
+ focusOffset: s.focusOffset
83
+ };
84
+ }
85
+ }
86
+ }
50
87
 
51
- // closest, matches, and remove polyfill
52
- // https://github.com/jonathantneal/closest
53
- (function (ELEMENT) {
54
- ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector || function matches(selector) {
55
- var
56
- element = this,
57
- elements = (element.document || element.ownerDocument).querySelectorAll(selector),
58
- index = 0;
88
+ e.setAttribute('contenteditable', 'true');
89
+ e.appendChild(t1);
90
+ e.appendChild(t2);
91
+ e.appendChild(t3);
92
+ doc.body.appendChild(e);
93
+ r.setStart(t2, 1);
94
+ r.setEnd(t2, 1);
59
95
 
60
- while (elements[index] && elements[index] !== element) {
61
- ++index;
62
- }
96
+ s = win.getSelection();
97
+ s.removeAllRanges();
98
+ s.addRange(r);
99
+ e.normalize();
100
+ s = win.getSelection();
63
101
 
64
- return elements[index] ? true : false;
65
- };
102
+ ret = (e.childNodes.length !== 1 || s.anchorNode !== e.firstChild || s.anchorOffset !== 2);
103
+ e.parentNode.removeChild(e);
104
+ s.removeAllRanges();
66
105
 
67
- ELEMENT.closest = ELEMENT.closest || function closest(selector) {
68
- var element = this;
106
+ if (originalTarget) {
107
+ if (originalTarget.type === 'form') {
108
+ // The selection parameters are not present for all form elements
109
+ if (typeof originalTarget.start !== 'undefined' && typeof originalTarget.end !== 'undefined') {
110
+ originalTarget.node.setSelectionRange(originalTarget.start, originalTarget.end);
111
+ }
112
+ originalTarget.node.focus();
113
+ } else if (originalTarget.type === 'range') {
114
+ r = doc.createRange();
115
+ r.setStart(originalTarget.anchorNode, originalTarget.anchorOffset);
116
+ r.setEnd(originalTarget.focusNode, originalTarget.focusOffset);
117
+ s.addRange(r);
118
+ }
119
+ }
69
120
 
70
- while (element) {
71
- if (element.matches(selector)) {
72
- break;
121
+ if (scrollTop !== window.pageYOffset || scrollLeft !== window.pageXOffset) {
122
+ win.scrollTo(scrollLeft, scrollTop);
73
123
  }
74
124
 
75
- element = element.parentElement;
125
+ return ret;
76
126
  }
127
+ },
77
128
 
78
- return element;
79
- };
129
+ apply: function() {
130
+ // closest, matches, and remove polyfill
131
+ // https://github.com/jonathantneal/closest
132
+ (function (ELEMENT) {
133
+ ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector || function matches(selector) {
134
+ var
135
+ element = this,
136
+ elements = (element.document || element.ownerDocument).querySelectorAll(selector),
137
+ index = 0;
80
138
 
81
- ELEMENT.remove = ELEMENT.remove || function remove() {
82
- if (this.parentNode) {
83
- this.parentNode.removeChild(this);
84
- }
85
- };
139
+ while (elements[index] && elements[index] !== element) {
140
+ ++index;
141
+ }
86
142
 
87
- }(win.Element.prototype));
143
+ return elements[index] ? true : false;
144
+ };
88
145
 
89
- if (!('classList' in doc.documentElement) && win.Object.defineProperty && typeof HTMLElement !== 'undefined') {
90
- win.Object.defineProperty(HTMLElement.prototype, 'classList', {
91
- get: function() {
92
- var self = this;
93
- function update(fn) {
94
- return function(value) {
95
- var classes = self.className.split(/\s+/),
96
- index = classes.indexOf(value);
146
+ ELEMENT.closest = ELEMENT.closest || function closest(selector) {
147
+ var element = this;
97
148
 
98
- fn(classes, index, value);
99
- self.className = classes.join(' ');
100
- };
101
- }
149
+ while (element) {
150
+ if (element.matches(selector)) {
151
+ break;
152
+ }
102
153
 
103
- var ret = {
104
- add: update(function(classes, index, value) {
105
- ~index || classes.push(value);
106
- }),
154
+ element = element.parentElement;
155
+ }
107
156
 
108
- remove: update(function(classes, index) {
109
- ~index && classes.splice(index, 1);
110
- }),
157
+ return element;
158
+ };
111
159
 
112
- toggle: update(function(classes, index, value) {
113
- ~index ? classes.splice(index, 1) : classes.push(value);
114
- }),
160
+ ELEMENT.remove = ELEMENT.remove || function remove() {
161
+ if (this.parentNode) {
162
+ this.parentNode.removeChild(this);
163
+ }
164
+ };
115
165
 
116
- contains: function(value) {
117
- return !!~self.className.split(/\s+/).indexOf(value);
118
- },
166
+ }(win.Element.prototype));
119
167
 
120
- item: function(i) {
121
- return self.className.split(/\s+/)[i] || null;
168
+ if (!('classList' in doc.documentElement) && win.Object.defineProperty && typeof win.HTMLElement !== 'undefined') {
169
+ win.Object.defineProperty(win.HTMLElement.prototype, 'classList', {
170
+ get: function() {
171
+ var self = this;
172
+ function update(fn) {
173
+ return function(value) {
174
+ var classes = self.className.split(/\s+/),
175
+ index = classes.indexOf(value);
176
+
177
+ fn(classes, index, value);
178
+ self.className = classes.join(' ');
179
+ };
122
180
  }
123
- };
124
181
 
125
- win.Object.defineProperty(ret, 'length', {
126
- get: function() {
127
- return self.className.split(/\s+/).length;
182
+ var ret = {
183
+ add: update(function(classes, index, value) {
184
+ ~index || classes.push(value);
185
+ }),
186
+
187
+ remove: update(function(classes, index) {
188
+ ~index && classes.splice(index, 1);
189
+ }),
190
+
191
+ toggle: update(function(classes, index, value) {
192
+ ~index ? classes.splice(index, 1) : classes.push(value);
193
+ }),
194
+
195
+ contains: function(value) {
196
+ return !!~self.className.split(/\s+/).indexOf(value);
197
+ },
198
+
199
+ item: function(i) {
200
+ return self.className.split(/\s+/)[i] || null;
201
+ }
202
+ };
203
+
204
+ win.Object.defineProperty(ret, 'length', {
205
+ get: function() {
206
+ return self.className.split(/\s+/).length;
207
+ }
208
+ });
209
+
210
+ return ret;
128
211
  }
129
212
  });
130
-
131
- return ret;
132
213
  }
133
- });
134
- }
135
214
 
136
- // Safary has a bug of not restoring selection after node.normalize correctly.
137
- // Detects the misbegaviour and patches it
138
- var normalizeHasCaretError = function() {
139
- if ("createRange" in doc && "getSelection" in win) {
140
- var e = doc.createElement('div'),
141
- t1 = doc.createTextNode('a'),
142
- t2 = doc.createTextNode('a'),
143
- t3 = doc.createTextNode('a'),
144
- r = doc.createRange(),
145
- s, ret;
146
-
147
- e.setAttribute('contenteditable', 'true');
148
- e.appendChild(t1);
149
- e.appendChild(t2);
150
- e.appendChild(t3);
151
- doc.body.appendChild(e);
152
- r.setStart(t2, 1);
153
- r.setEnd(t2, 1);
154
-
155
- s = win.getSelection();
156
- s.removeAllRanges();
157
- s.addRange(r);
158
- e.normalize();
159
- s = win.getSelection();
160
-
161
- ret = (e.childNodes.length !== 1 || s.anchorNode !== e.firstChild || s.anchorOffset !== 2);
162
- e.parentNode.removeChild(e);
163
- s.removeAllRanges();
164
- return ret;
165
- }
166
- };
215
+ var getTextNodes = function(node){
216
+ var all = [];
217
+ for (node=node.firstChild;node;node=node.nextSibling){
218
+ if (node.nodeType == 3) {
219
+ all.push(node);
220
+ } else {
221
+ all = all.concat(getTextNodes(node));
222
+ }
223
+ }
224
+ return all;
225
+ };
167
226
 
168
- var getTextNodes = function(node){
169
- var all = [];
170
- for (node=node.firstChild;node;node=node.nextSibling){
171
- if (node.nodeType == 3) {
172
- all.push(node);
173
- } else {
174
- all = all.concat(getTextNodes(node));
175
- }
176
- }
177
- return all;
178
- };
227
+ var isInDom = function(node) {
228
+ var doc = node.ownerDocument,
229
+ n = node;
230
+
231
+ do {
232
+ if (n === doc) {
233
+ return true;
234
+ }
235
+ n = n.parentNode;
236
+ } while(n);
179
237
 
180
- var isInDom = function(node) {
181
- var doc = node.ownerDocument,
182
- n = node;
238
+ return false;
239
+ };
183
240
 
184
- do {
185
- if (n === doc) {
186
- return true;
187
- }
188
- n = n.parentNode;
189
- } while(n);
241
+ var normalizeFix = function() {
242
+ var f = win.Node.prototype.normalize;
243
+ var nf = function() {
244
+ var texts = getTextNodes(this),
245
+ s = this.ownerDocument.defaultView.getSelection(),
246
+ anode = s.anchorNode,
247
+ aoffset = s.anchorOffset,
248
+ aelement = anode && anode.nodeType === 1 && anode.childNodes.length > 0 ? anode.childNodes[aoffset] : undefined,
249
+ fnode = s.focusNode,
250
+ foffset = s.focusOffset,
251
+ felement = fnode && fnode.nodeType === 1 && foffset > 0 ? fnode.childNodes[foffset -1] : undefined,
252
+ r = this.ownerDocument.createRange(),
253
+ prevTxt = texts.shift(),
254
+ curText = prevTxt ? texts.shift() : null;
255
+
256
+ if (felement && felement.nodeType === 3) {
257
+ fnode = felement;
258
+ foffset = felement.nodeValue.length;
259
+ felement = undefined;
260
+ }
190
261
 
191
- return false;
192
- };
262
+ if (aelement && aelement.nodeType === 3) {
263
+ anode = aelement;
264
+ aoffset = 0;
265
+ aelement = undefined;
266
+ }
193
267
 
194
- var normalizeFix = function() {
195
- var f = win.Node.prototype.normalize;
196
- var nf = function() {
197
- var texts = getTextNodes(this),
198
- s = this.ownerDocument.defaultView.getSelection(),
199
- anode = s.anchorNode,
200
- aoffset = s.anchorOffset,
201
- aelement = anode && anode.nodeType === 1 && anode.childNodes.length > 0 ? anode.childNodes[aoffset] : undefined,
202
- fnode = s.focusNode,
203
- foffset = s.focusOffset,
204
- felement = fnode && fnode.nodeType === 1 && foffset > 0 ? fnode.childNodes[foffset -1] : undefined,
205
- r = this.ownerDocument.createRange(),
206
- prevTxt = texts.shift(),
207
- curText = prevTxt ? texts.shift() : null;
208
-
209
- if (felement && felement.nodeType === 3) {
210
- fnode = felement;
211
- foffset = felement.nodeValue.length;
212
- felement = undefined;
213
- }
214
-
215
- if (aelement && aelement.nodeType === 3) {
216
- anode = aelement;
217
- aoffset = 0;
218
- aelement = undefined;
219
- }
220
-
221
- if ((anode === fnode && foffset < aoffset) || (anode !== fnode && (anode.compareDocumentPosition(fnode) & win.Node.DOCUMENT_POSITION_PRECEDING) && !(anode.compareDocumentPosition(fnode) & win.Node.DOCUMENT_POSITION_CONTAINS))) {
222
- fnode = [anode, anode = fnode][0];
223
- foffset = [aoffset, aoffset = foffset][0];
224
- }
225
-
226
- while(prevTxt && curText) {
227
- if (curText.previousSibling && curText.previousSibling === prevTxt) {
228
- if (anode === curText) {
229
- anode = prevTxt;
230
- aoffset = prevTxt.nodeValue.length + aoffset;
268
+ if ((anode === fnode && foffset < aoffset) || (anode !== fnode && (anode.compareDocumentPosition(fnode) & win.Node.DOCUMENT_POSITION_PRECEDING) && !(anode.compareDocumentPosition(fnode) & win.Node.DOCUMENT_POSITION_CONTAINS))) {
269
+ fnode = [anode, anode = fnode][0];
270
+ foffset = [aoffset, aoffset = foffset][0];
231
271
  }
232
- if (fnode === curText) {
233
- fnode = prevTxt;
234
- foffset = prevTxt.nodeValue.length + foffset;
272
+
273
+ while(prevTxt && curText) {
274
+ if (curText.previousSibling && curText.previousSibling === prevTxt) {
275
+ if (anode === curText) {
276
+ anode = prevTxt;
277
+ aoffset = prevTxt.nodeValue.length + aoffset;
278
+ }
279
+ if (fnode === curText) {
280
+ fnode = prevTxt;
281
+ foffset = prevTxt.nodeValue.length + foffset;
282
+ }
283
+ prevTxt.nodeValue = prevTxt.nodeValue + curText.nodeValue;
284
+ curText.parentNode.removeChild(curText);
285
+ curText = texts.shift();
286
+ } else {
287
+ prevTxt = curText;
288
+ curText = texts.shift();
289
+ }
235
290
  }
236
- prevTxt.nodeValue = prevTxt.nodeValue + curText.nodeValue;
237
- curText.parentNode.removeChild(curText);
238
- curText = texts.shift();
239
- } else {
240
- prevTxt = curText;
241
- curText = texts.shift();
291
+
292
+ if (felement) {
293
+ foffset = Array.prototype.indexOf.call(felement.parentNode.childNodes, felement) + 1;
294
+ }
295
+
296
+ if (aelement) {
297
+ aoffset = Array.prototype.indexOf.call(aelement.parentNode.childNodes, aelement);
298
+ }
299
+
300
+ if (isInDom(this) && anode && anode.parentNode && fnode && fnode.parentNode) {
301
+ r.setStart(anode, aoffset);
302
+ r.setEnd(fnode, foffset);
303
+ s.removeAllRanges();
304
+ s.addRange(r);
305
+ }
306
+ };
307
+ win.Node.prototype.normalize = nf;
308
+ };
309
+
310
+ var F = function() {
311
+ win.removeEventListener("load", F);
312
+ if ("Node" in win && "normalize" in win.Node.prototype && methods.normalizeHasCaretError()) {
313
+ normalizeFix();
242
314
  }
243
- }
315
+ };
244
316
 
245
- if (felement) {
246
- foffset = Array.prototype.indexOf.call(felement.parentNode.childNodes, felement) + 1;
317
+ if (doc.readyState !== "complete") {
318
+ win.addEventListener("load", F);
319
+ } else {
320
+ F();
247
321
  }
248
322
 
249
- if (aelement) {
250
- aoffset = Array.prototype.indexOf.call(aelement.parentNode.childNodes, aelement);
323
+ // CustomEvent for ie9 and up
324
+ function nativeCustomEventSupported() {
325
+ try {
326
+ var p = new win.CustomEvent('cat', {detail: {foo: 'bar'}});
327
+ return 'cat' === p.type && 'bar' === p.detail.foo;
328
+ } catch (e) {}
329
+ return false;
251
330
  }
252
331
 
253
- if (isInDom(this) && anode && anode.parentNode && fnode && fnode.parentNode) {
254
- r.setStart(anode, aoffset);
255
- r.setEnd(fnode, foffset);
256
- s.removeAllRanges();
257
- s.addRange(r);
258
- }
259
- };
260
- win.Node.prototype.normalize = nf;
261
- };
262
-
263
- var F = function() {
264
- win.removeEventListener("load", F);
265
- if ("Node" in win && "normalize" in win.Node.prototype && normalizeHasCaretError()) {
266
- normalizeFix();
332
+ // Polyfills CustomEvent object for IE9 and up
333
+ (function() {
334
+ if (!nativeCustomEventSupported() && "CustomEvent" in win) {
335
+ function CustomEvent(event, params) {
336
+ params = params || {bubbles: false, cancelable: false, detail: undefined};
337
+ var evt = doc.createEvent('CustomEvent');
338
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
339
+ return evt;
340
+ }
341
+ CustomEvent.prototype = win.Event.prototype;
342
+ win.CustomEvent = CustomEvent;
343
+ }
344
+ })();
267
345
  }
268
- };
269
-
270
- if (doc.readyState !== "complete") {
271
- win.addEventListener("load", F);
272
- } else {
273
- F();
274
346
  }
275
347
 
276
- // CustomEvent for ie9 and up
277
- function nativeCustomEventSupported() {
278
- try {
279
- var p = new CustomEvent('cat', {detail: {foo: 'bar'}});
280
- return 'cat' === p.type && 'bar' === p.detail.foo;
281
- } catch (e) {}
282
- return false;
283
- }
284
- var customEventSupported = nativeCustomEventSupported();
285
-
286
- // Polyfills CustomEvent object for IE9 and up
287
- (function() {
288
- if (!customEventSupported && "CustomEvent" in win) {
289
- function CustomEvent(event, params) {
290
- params = params || {bubbles: false, cancelable: false, detail: undefined};
291
- var evt = doc.createEvent('CustomEvent');
292
- evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
293
- return evt;
294
- }
295
- CustomEvent.prototype = win.Event.prototype;
296
- win.CustomEvent = CustomEvent;
297
- customEventSupported = true;
298
- }
299
- })();
348
+ return methods;
300
349
  };
301
350
 
302
- wysihtml.polyfills(window, document);
351
+ wysihtml.polyfills(window, document).apply();
303
352
 
304
353
  /*
305
354
  Base.js, version 1.1a
@@ -313,7 +362,7 @@ var Base = function() {
313
362
 
314
363
  Base.extend = function(_instance, _static) { // subclass
315
364
  var extend = Base.prototype.extend;
316
-
365
+
317
366
  // build the prototype
318
367
  Base._prototyping = true;
319
368
  var proto = new this;
@@ -322,7 +371,7 @@ Base.extend = function(_instance, _static) { // subclass
322
371
  // call this method from any other method to invoke that method's ancestor
323
372
  };
324
373
  delete Base._prototyping;
325
-
374
+
326
375
  // create the wrapper for the constructor function
327
376
  //var constructor = proto.constructor.valueOf(); //-dean
328
377
  var constructor = proto.constructor;
@@ -337,7 +386,7 @@ Base.extend = function(_instance, _static) { // subclass
337
386
  }
338
387
  }
339
388
  };
340
-
389
+
341
390
  // build the class interface
342
391
  klass.ancestor = this;
343
392
  klass.extend = this.extend;
@@ -355,7 +404,7 @@ Base.extend = function(_instance, _static) { // subclass
355
404
  return klass;
356
405
  };
357
406
 
358
- Base.prototype = {
407
+ Base.prototype = {
359
408
  extend: function(source, value) {
360
409
  if (arguments.length > 1) { // extending with a name/value pair
361
410
  var ancestor = this[source];
@@ -414,7 +463,7 @@ Base = Base.extend({
414
463
  }, {
415
464
  ancestor: Object,
416
465
  version: "1.1",
417
-
466
+
418
467
  forEach: function(object, block, context) {
419
468
  for (var key in object) {
420
469
  if (this.prototype[key] === undefined) {
@@ -422,7 +471,7 @@ Base = Base.extend({
422
471
  }
423
472
  }
424
473
  },
425
-
474
+
426
475
  implement: function() {
427
476
  for (var i = 0; i < arguments.length; i++) {
428
477
  if (typeof arguments[i] == "function") {
@@ -435,7 +484,7 @@ Base = Base.extend({
435
484
  }
436
485
  return this;
437
486
  },
438
-
487
+
439
488
  toString: function() {
440
489
  return String(this.valueOf());
441
490
  }
@@ -4244,7 +4293,7 @@ var rangy;
4244
4293
  win = null;
4245
4294
  });
4246
4295
  });
4247
-
4296
+
4248
4297
 
4249
4298
  /*----------------------------------------------------------------------------------------------------------------*/
4250
4299
 
@@ -6849,6 +6898,14 @@ wysihtml.browser = (function() {
6849
6898
 
6850
6899
  usesControlRanges: function() {
6851
6900
  return document.body && "createControlRange" in document.body;
6901
+ },
6902
+
6903
+ // Webkit browsers have an issue that when caret is at the end of link it is moved outside of link while inserting new characters,
6904
+ // so all inserted content will be after link. Selection before inserion is reported to be in link though.
6905
+ // This makes changing link texts from problematic to impossible (if link is just 1 characer long) for the user.
6906
+ // TODO: needs to be tested better than just browser as it some day might get fixed
6907
+ hasCaretAtLinkEndInsertionProblems: function() {
6908
+ return isWebKit;
6852
6909
  }
6853
6910
  };
6854
6911
  })();
@@ -7674,7 +7731,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7674
7731
  return {
7675
7732
  from: function(elementToCopyFrom) {
7676
7733
  return {
7677
- to: function(elementToCopyTo) {
7734
+ to: function pasteElementAttributesTo(elementToCopyTo) {
7678
7735
  var attribute,
7679
7736
  i = 0,
7680
7737
  length = attributesToCopy.length;
@@ -7684,7 +7741,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7684
7741
  elementToCopyTo[attribute] = elementToCopyFrom[attribute];
7685
7742
  }
7686
7743
  }
7687
- return { andTo: arguments.callee };
7744
+ return { andTo: pasteElementAttributesTo };
7688
7745
  }
7689
7746
  };
7690
7747
  }
@@ -7755,9 +7812,9 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7755
7812
  }
7756
7813
 
7757
7814
  return {
7758
- to: function(element) {
7815
+ to: function pasteStylesTo(element) {
7759
7816
  dom.setStyles(cssText).on(element);
7760
- return { andTo: arguments.callee };
7817
+ return { andTo: pasteStylesTo };
7761
7818
  }
7762
7819
  };
7763
7820
  }
@@ -7858,7 +7915,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7858
7915
  prev: function(options) {
7859
7916
  var prevNode = node.previousSibling,
7860
7917
  types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
7861
-
7918
+
7862
7919
  if (!prevNode) {
7863
7920
  return null;
7864
7921
  }
@@ -7870,7 +7927,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7870
7927
  ) {
7871
7928
  return wysihtml.dom.domNode(prevNode).prev(options);
7872
7929
  }
7873
-
7930
+
7874
7931
  return prevNode;
7875
7932
  },
7876
7933
 
@@ -7878,7 +7935,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7878
7935
  next: function(options) {
7879
7936
  var nextNode = node.nextSibling,
7880
7937
  types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
7881
-
7938
+
7882
7939
  if (!nextNode) {
7883
7940
  return null;
7884
7941
  }
@@ -7890,7 +7947,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7890
7947
  ) {
7891
7948
  return wysihtml.dom.domNode(nextNode).next(options);
7892
7949
  }
7893
-
7950
+
7894
7951
  return nextNode;
7895
7952
  },
7896
7953
 
@@ -7953,7 +8010,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
7953
8010
  escapeParent: function(element, newWrapper) {
7954
8011
  var parent, split2, nodeWrap,
7955
8012
  curNode = node;
7956
-
8013
+
7957
8014
  // Stop if node is not a descendant of element
7958
8015
  if (!wysihtml.dom.contains(element, node)) {
7959
8016
  throw new Error("Child is not a descendant of node.");
@@ -8036,7 +8093,7 @@ wysihtml.dom.copyAttributes = function(attributesToCopy) {
8036
8093
  Tests on principle that all properties defined must have at least one match.
8037
8094
  styleValue parameter works in context of styleProperty and has no effect otherwise.
8038
8095
  Returns true if element matches and false if it does not.
8039
-
8096
+
8040
8097
  Properties for filtering element:
8041
8098
  {
8042
8099
  query: selector string,
@@ -8326,12 +8383,12 @@ wysihtml.dom.getParentElement = (function() {
8326
8383
 
8327
8384
  })();
8328
8385
 
8329
- /*
8386
+ /*
8330
8387
  * Methods for fetching pasted html before it gets inserted into content
8331
8388
  **/
8332
8389
 
8333
8390
  /* Modern event.clipboardData driven approach.
8334
- * Advantage is that it does not have to loose selection or modify dom to catch the data.
8391
+ * Advantage is that it does not have to loose selection or modify dom to catch the data.
8335
8392
  * IE does not support though.
8336
8393
  **/
8337
8394
  wysihtml.dom.getPastedHtml = function(event) {
@@ -8352,7 +8409,7 @@ wysihtml.dom.getPastedHtmlWithDiv = function (composer, f) {
8352
8409
  doc = composer.element.ownerDocument,
8353
8410
  cleanerDiv = doc.createElement('DIV'),
8354
8411
  scrollPos = composer.getScrollPos();
8355
-
8412
+
8356
8413
  doc.body.appendChild(cleanerDiv);
8357
8414
 
8358
8415
  cleanerDiv.style.width = "1px";
@@ -9650,7 +9707,7 @@ wysihtml.dom.renameElement = function(element, newNodeName) {
9650
9707
  newElement.appendChild(firstChild);
9651
9708
  }
9652
9709
  wysihtml.dom.copyAttributes(["align", "className"]).from(element).to(newElement);
9653
-
9710
+
9654
9711
  if (element.parentNode) {
9655
9712
  element.parentNode.replaceChild(newElement, element);
9656
9713
  }
@@ -9981,7 +10038,7 @@ wysihtml.dom.replaceWithChildNodes = function(node) {
9981
10038
  }
9982
10039
 
9983
10040
  if (wysihtml.polyfills) {
9984
- wysihtml.polyfills(iframeWindow, iframeDocument);
10041
+ wysihtml.polyfills(iframeWindow, iframeDocument).apply();
9985
10042
  }
9986
10043
 
9987
10044
  this.loaded = true;
@@ -10333,7 +10390,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
10333
10390
  })(wysihtml);
10334
10391
 
10335
10392
  (function(wysihtml) {
10336
-
10393
+
10337
10394
  // List of supported color format parsing methods
10338
10395
  // If radix is not defined 10 is expected as default
10339
10396
  var colorParseMethods = {
@@ -10376,7 +10433,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
10376
10433
  }
10377
10434
  }
10378
10435
 
10379
- // Takes color string value ("#abc", "rgb(1,2,3)", ...) as an argument and returns the type of that color format "hex", "rgb", "rgba".
10436
+ // Takes color string value ("#abc", "rgb(1,2,3)", ...) as an argument and returns the type of that color format "hex", "rgb", "rgba".
10380
10437
  function getColorFormat (colorStr) {
10381
10438
  var type = getColorParseMethod(colorStr);
10382
10439
 
@@ -10389,9 +10446,9 @@ wysihtml.quirks.ensureProperClearing = (function() {
10389
10446
  // Takes color string value as an argument and returns suitable parsing method for it
10390
10447
  getColorParseMethod : getColorParseMethod,
10391
10448
 
10392
- // Takes color string value as an argument and returns the type of that color format "hex", "rgb", "rgba".
10449
+ // Takes color string value as an argument and returns the type of that color format "hex", "rgb", "rgba".
10393
10450
  getColorFormat : getColorFormat,
10394
-
10451
+
10395
10452
  /* Parses a color string to and array of [red, green, blue, alpha].
10396
10453
  * paramName: optional argument to parse color value directly from style string parameter
10397
10454
  *
@@ -10731,7 +10788,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
10731
10788
  if (!sel || (lastSibling === node && node.nodeType === 1 && win.getComputedStyle(node).display === "block")) {
10732
10789
  if (notVisual) {
10733
10790
  // If setAfter is used as internal between actions, self-removing caretPlaceholder has simpler implementation
10734
- // and remove itself in call stack end instead on user interaction
10791
+ // and remove itself in call stack end instead on user interaction
10735
10792
  var caretPlaceholder = this.doc.createTextNode(wysihtml.INVISIBLE_SPACE);
10736
10793
  node.parentNode.insertBefore(caretPlaceholder, node.nextSibling);
10737
10794
  this.selectNode(caretPlaceholder);
@@ -10898,11 +10955,11 @@ wysihtml.quirks.ensureProperClearing = (function() {
10898
10955
  this.deleteRangeContents(range);
10899
10956
  this.setSelection(range);
10900
10957
  },
10901
-
10958
+
10902
10959
  // Makes sure all uneditable sare notified before deleting contents
10903
10960
  deleteRangeContents: function (range) {
10904
10961
  var startParent, endParent, uneditables, ev;
10905
-
10962
+
10906
10963
  if (this.unselectableClass) {
10907
10964
  if ((startParent = wysihtml.dom.getParentElement(range.startContainer, { query: "." + this.unselectableClass }, false, this.contain))) {
10908
10965
  range.setStartBefore(startParent);
@@ -10998,7 +11055,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
10998
11055
  if (r[0].startOffset === 0 && r[0].startContainer.previousSibling) {
10999
11056
  caretNode = r[0].startContainer.previousSibling;
11000
11057
  if (caretNode.nodeType === 3) {
11001
- offset = caretNode.data.length;
11058
+ offset = caretNode.data.length;
11002
11059
  }
11003
11060
  } else {
11004
11061
  caretNode = r[0].startContainer;
@@ -11033,6 +11090,22 @@ wysihtml.quirks.ensureProperClearing = (function() {
11033
11090
  },
11034
11091
 
11035
11092
  getRangeToNodeEnd: function() {
11093
+ if (this.isCollapsed()) {
11094
+ var range = this.getRange(),
11095
+ sNode, pos, lastR;
11096
+ if (range) {
11097
+ sNode = range.startContainer;
11098
+ pos = range.startOffset;
11099
+ lastR = rangy.createRange(this.doc);
11100
+
11101
+ lastR.selectNodeContents(sNode);
11102
+ lastR.setStart(sNode, pos);
11103
+ return lastR;
11104
+ }
11105
+ }
11106
+ },
11107
+
11108
+ getRangeToNodeBeginning: function() {
11036
11109
  if (this.isCollapsed()) {
11037
11110
  var range = this.getRange(),
11038
11111
  sNode = range.startContainer,
@@ -11040,18 +11113,36 @@ wysihtml.quirks.ensureProperClearing = (function() {
11040
11113
  lastR = rangy.createRange(this.doc);
11041
11114
 
11042
11115
  lastR.selectNodeContents(sNode);
11043
- lastR.setStart(sNode, pos);
11116
+ lastR.setEnd(sNode, pos);
11044
11117
  return lastR;
11045
11118
  }
11046
11119
  },
11047
11120
 
11048
- caretIsLastInSelection: function() {
11121
+ // This function returns if caret is last in a node (no textual visible content follows)
11122
+ caretIsInTheEndOfNode: function(ignoreIfSpaceIsBeforeCaret) {
11049
11123
  var r = rangy.createRange(this.doc),
11050
11124
  s = this.getSelection(),
11051
- endc = this.getRangeToNodeEnd().cloneContents(),
11052
- endtxt = endc.textContent;
11053
-
11054
- return (/^\s*$/).test(endtxt);
11125
+ rangeToNodeEnd = this.getRangeToNodeEnd(),
11126
+ endc, endtxt, beginc, begintxt;
11127
+
11128
+ if (rangeToNodeEnd) {
11129
+ endc = rangeToNodeEnd.cloneContents();
11130
+ endtxt = endc.textContent;
11131
+
11132
+ if ((/^\s*$/).test(endtxt)) {
11133
+ if (ignoreIfSpaceIsBeforeCaret) {
11134
+ beginc = this.getRangeToNodeBeginning().cloneContents();
11135
+ begintxt = beginc.textContent;
11136
+ return !(/[\u00A0 ][\s\uFEFF]*$/).test(begintxt);
11137
+ } else {
11138
+ return true;
11139
+ }
11140
+ } else {
11141
+ return false;
11142
+ }
11143
+ } else {
11144
+ return false;
11145
+ }
11055
11146
  },
11056
11147
 
11057
11148
  caretIsFirstInSelection: function(includeLineBreaks) {
@@ -11059,7 +11150,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11059
11150
  s = this.getSelection(),
11060
11151
  range = this.getRange(),
11061
11152
  startNode = getRangeNode(range.startContainer, range.startOffset);
11062
-
11153
+
11063
11154
  if (startNode) {
11064
11155
  if (startNode.nodeType === wysihtml.TEXT_NODE) {
11065
11156
  if (!startNode.parentNode) {
@@ -11284,7 +11375,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11284
11375
  node = this.doc.createElement('DIV'),
11285
11376
  fragment = this.doc.createDocumentFragment(),
11286
11377
  lastChild, lastEditorElement;
11287
-
11378
+
11288
11379
  if (range) {
11289
11380
  range.deleteContents();
11290
11381
  node.innerHTML = html;
@@ -11294,7 +11385,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11294
11385
  fragment.appendChild(node.firstChild);
11295
11386
  }
11296
11387
  range.insertNode(fragment);
11297
-
11388
+
11298
11389
  lastEditorElement = this.contain.lastChild;
11299
11390
  while (lastEditorElement && lastEditorElement.nodeType === 3 && lastEditorElement.previousSibling && (/^\s*$/).test(lastEditorElement.data)) {
11300
11391
  lastEditorElement = lastEditorElement.previousSibling;
@@ -11320,6 +11411,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11320
11411
  insertNode: function(node) {
11321
11412
  var range = this.getRange();
11322
11413
  if (range) {
11414
+ range.deleteContents();
11323
11415
  range.insertNode(node);
11324
11416
  }
11325
11417
  },
@@ -11464,7 +11556,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11464
11556
  this._selectLineUniversal();
11465
11557
  }
11466
11558
  },
11467
-
11559
+
11468
11560
  includeRangyRangeHelpers: function() {
11469
11561
  var s = this.getSelection(),
11470
11562
  r = s.getRangeAt(0),
@@ -11480,7 +11572,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11480
11572
  },
11481
11573
  anode = s.anchorNode.nodeType === 1 ? s.anchorNode.childNodes[s.anchorOffset] : s.anchorNode,
11482
11574
  fnode = s.focusNode.nodeType === 1 ? s.focusNode.childNodes[s.focusOffset] : s.focusNode;
11483
-
11575
+
11484
11576
  if (fnode && s.focusOffset === getNodeLength(fnode) && fnode.nextSibling && isHelperNode(fnode.nextSibling)) {
11485
11577
  r.setEndAfter(fnode.nextSibling);
11486
11578
  }
@@ -11496,10 +11588,10 @@ wysihtml.quirks.ensureProperClearing = (function() {
11496
11588
  _selectLine_W3C: function() {
11497
11589
  var selection = this.win.getSelection(),
11498
11590
  initialBoundry = [selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset];
11499
-
11591
+
11500
11592
  selection.modify("move", "left", "lineboundary");
11501
11593
  selection.modify("extend", "right", "lineboundary");
11502
-
11594
+
11503
11595
  // IF lineboundary extending did not change selection try universal fallback (FF fails sometimes without a reason)
11504
11596
  if (selection.anchorNode === initialBoundry[0] &&
11505
11597
  selection.anchorOffset === initialBoundry[1] &&
@@ -11601,14 +11693,14 @@ wysihtml.quirks.ensureProperClearing = (function() {
11601
11693
  if (!r.collapsed) {
11602
11694
  r.insertNode(this.doc.createTextNode(wysihtml.INVISIBLE_SPACE));
11603
11695
  }
11604
-
11696
+
11605
11697
  // Is probably just empty line as can not be expanded
11606
11698
  rect = r.nativeRange.getBoundingClientRect();
11607
11699
  // If startnode is not line break allready move the start position of range by -1 character until clientRect top changes;
11608
11700
  do {
11609
11701
  amount = r.moveStart('character', -1);
11610
11702
  testRect = r.nativeRange.getBoundingClientRect();
11611
-
11703
+
11612
11704
  if (!testRect || Math.floor(testRect.top) !== Math.floor(rect.top)) {
11613
11705
  r.moveStart('character', 1);
11614
11706
  found = true;
@@ -11618,7 +11710,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11618
11710
  count = 0;
11619
11711
  found = false;
11620
11712
  rect = r.nativeRange.getBoundingClientRect();
11621
-
11713
+
11622
11714
  if (r.endContainer !== this.contain || (this.contain.lastChild && this.contain.childNodes[r.endOffset] !== this.contain.lastChild)) {
11623
11715
  do {
11624
11716
  amount = r.moveEnd('character', 1);
@@ -11812,7 +11904,7 @@ wysihtml.quirks.ensureProperClearing = (function() {
11812
11904
 
11813
11905
  wysihtml.dom.removeInvisibleSpaces(this.composer.element);
11814
11906
  doSelect();
11815
-
11907
+
11816
11908
  if (this.composer.element.firstChild && notSelected()) {
11817
11909
  // Try fixing end
11818
11910
  this.composer.element.appendChild(blankEndNode);
@@ -11821,11 +11913,11 @@ wysihtml.quirks.ensureProperClearing = (function() {
11821
11913
  if (notSelected()) {
11822
11914
  // Remove end fix
11823
11915
  blankEndNode.parentNode.removeChild(blankEndNode);
11824
-
11916
+
11825
11917
  // Try fixing beginning
11826
11918
  this.composer.element.insertBefore(blankStartNode, this.composer.element.firstChild);
11827
11919
  doSelect();
11828
-
11920
+
11829
11921
  if (notSelected()) {
11830
11922
  // Try fixing both
11831
11923
  this.composer.element.appendChild(blankEndNode);
@@ -11960,7 +12052,7 @@ wysihtml.Commands = Base.extend(
11960
12052
  result = null;
11961
12053
 
11962
12054
  // If composer ahs placeholder unset it before command
11963
- // Do not apply on commands that are behavioral
12055
+ // Do not apply on commands that are behavioral
11964
12056
  if (this.composer.hasPlaceholderSet() && !wysihtml.lang.array(['styleWithCSS', 'enableObjectResizing', 'enableInlineTableEditing']).contains(command)) {
11965
12057
  this.composer.element.innerHTML = "";
11966
12058
  this.composer.selection.selectNode(this.composer.element);
@@ -12065,9 +12157,9 @@ wysihtml.Commands = Base.extend(
12065
12157
  })(wysihtml);
12066
12158
 
12067
12159
  /* Formatblock
12068
- * Is used to insert block level elements
12160
+ * Is used to insert block level elements
12069
12161
  * It tries to solve the case that some block elements should not contain other block level elements (h1-6, p, ...)
12070
- *
12162
+ *
12071
12163
  */
12072
12164
  (function(wysihtml) {
12073
12165
 
@@ -12135,7 +12227,7 @@ wysihtml.Commands = Base.extend(
12135
12227
  elements[i].parentNode.removeChild(elements[i]);
12136
12228
  }
12137
12229
  }
12138
-
12230
+
12139
12231
  return newBlockElements;
12140
12232
  }
12141
12233
 
@@ -12147,7 +12239,7 @@ wysihtml.Commands = Base.extend(
12147
12239
  function findOuterBlock(node, container, allBlocks) {
12148
12240
  var n = node,
12149
12241
  block = null;
12150
-
12242
+
12151
12243
  while (n && container && n !== container) {
12152
12244
  if (n.nodeType === 1 && n.matches(allBlocks ? BLOCK_ELEMENTS : UNNESTABLE_BLOCK_ELEMENTS)) {
12153
12245
  block = n;
@@ -12272,7 +12364,7 @@ wysihtml.Commands = Base.extend(
12272
12364
  for (var i = blocks.length; i--;) {
12273
12365
  nextEl = wysihtml.dom.domNode(blocks[i]).next({nodeTypes: [1,3], ignoreBlankTexts: true}),
12274
12366
  prevEl = wysihtml.dom.domNode(blocks[i]).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
12275
-
12367
+
12276
12368
  if (nextEl && nextEl.nodeType !== 1 && nextEl.nodeName !== 'BR') {
12277
12369
  if ((blocks[i].innerHTML || blocks[i].nodeValue || '').trim() !== '') {
12278
12370
  blocks[i].parentNode.insertBefore(blocks[i].ownerDocument.createElement('BR'), nextEl);
@@ -12314,7 +12406,7 @@ wysihtml.Commands = Base.extend(
12314
12406
  }
12315
12407
  return;
12316
12408
  }
12317
-
12409
+
12318
12410
  // If range ends outside of node and starts inside at textrange and covers the whole node visually, extend start to cover the node start too
12319
12411
  if (end && end.nodeType === 1 && start.nodeType === 3) {
12320
12412
  if (end.firstChild === start && range.startOffset === 0) {
@@ -12325,7 +12417,7 @@ wysihtml.Commands = Base.extend(
12325
12417
  return;
12326
12418
  }
12327
12419
 
12328
- // If range covers a whole textnode and the textnode is the only child of node, extend range to node
12420
+ // If range covers a whole textnode and the textnode is the only child of node, extend range to node
12329
12421
  if (start && start.nodeType === 3 && start === end && start.parentNode.childNodes.length === 1) {
12330
12422
  if (range.endOffset == end.data.length && range.startOffset === 0) {
12331
12423
  node = start.parentNode;
@@ -12337,16 +12429,16 @@ wysihtml.Commands = Base.extend(
12337
12429
  return;
12338
12430
  }
12339
12431
  }
12340
-
12432
+
12341
12433
  // Scans ranges array for insertion points that are not allowed to insert block tags fixes/splits illegal ranges
12342
12434
  // Some places do not allow block level elements inbetween (inside ul and outside li)
12343
12435
  // TODO: might need extending for other nodes besides li (maybe dd,dl,dt)
12344
12436
  function fixNotPermittedInsertionPoints(ranges) {
12345
12437
  var newRanges = [],
12346
12438
  lis, j, maxj, tmpRange, rangePos, closestLI;
12347
-
12439
+
12348
12440
  for (var i = 0, maxi = ranges.length; i < maxi; i++) {
12349
-
12441
+
12350
12442
  // Fixes range start and end positions if inside UL or OL element (outside of LI)
12351
12443
  if (ranges[i].startContainer.nodeType === 1 && ranges[i].startContainer.matches('ul, ol')) {
12352
12444
  ranges[i].setStart(ranges[i].startContainer.childNodes[ranges[i].startOffset], 0);
@@ -12366,7 +12458,7 @@ wysihtml.Commands = Base.extend(
12366
12458
  return node.nodeName === "LI";
12367
12459
  });
12368
12460
  if (lis.length > 0) {
12369
-
12461
+
12370
12462
  for (j = 0, maxj = lis.length; j < maxj; j++) {
12371
12463
  rangePos = ranges[i].compareNode(lis[j]);
12372
12464
 
@@ -12376,7 +12468,7 @@ wysihtml.Commands = Base.extend(
12376
12468
 
12377
12469
  tmpRange = ranges[i].cloneRange();
12378
12470
  closestLI = wysihtml.dom.domNode(lis[j]).prev({nodeTypes: [1]});
12379
-
12471
+
12380
12472
  if (closestLI) {
12381
12473
  tmpRange.setEnd(closestLI, closestLI.childNodes.length);
12382
12474
  } else if (lis[j].closest('ul, ol')) {
@@ -12387,16 +12479,16 @@ wysihtml.Commands = Base.extend(
12387
12479
  newRanges.push(tmpRange);
12388
12480
  ranges[i].setStart(lis[j], 0);
12389
12481
  }
12390
-
12482
+
12391
12483
  // Fixes end of range that crosses li border
12392
12484
  if (rangePos === ranges[i].NODE_BEFORE || rangePos === ranges[i].NODE_INSIDE) {
12393
12485
  // Range starts inside the node and ends after node
12394
-
12486
+
12395
12487
  tmpRange = ranges[i].cloneRange();
12396
12488
  tmpRange.setEnd(lis[j], lis[j].childNodes.length);
12397
12489
  newRanges.push(tmpRange);
12398
-
12399
- // Find next LI in list and if present set range to it, else
12490
+
12491
+ // Find next LI in list and if present set range to it, else
12400
12492
  closestLI = wysihtml.dom.domNode(lis[j]).next({nodeTypes: [1]});
12401
12493
  if (closestLI) {
12402
12494
  ranges[i].setStart(closestLI, 0);
@@ -12404,7 +12496,7 @@ wysihtml.Commands = Base.extend(
12404
12496
  ranges[i].setStartAfter(lis[j].closest('ul, ol'));
12405
12497
  } else {
12406
12498
  ranges[i].setStartAfter(lis[j]);
12407
- }
12499
+ }
12408
12500
  }
12409
12501
  }
12410
12502
  newRanges.push(ranges[i]);
@@ -12414,17 +12506,17 @@ wysihtml.Commands = Base.extend(
12414
12506
  }
12415
12507
  return newRanges;
12416
12508
  }
12417
-
12509
+
12418
12510
  // Return options object with nodeName set if original did not have any
12419
12511
  // Node name is set to local or global default
12420
12512
  function getOptionsWithNodename(options, defaultName, composer) {
12421
12513
  var correctedOptions = (options) ? wysihtml.lang.object(options).clone(true) : null;
12422
- if (correctedOptions) {
12514
+ if (correctedOptions) {
12423
12515
  correctedOptions.nodeName = correctedOptions.nodeName || defaultName || defaultNodeName(composer);
12424
12516
  }
12425
12517
  return correctedOptions;
12426
12518
  }
12427
-
12519
+
12428
12520
  // Injects document fragment to range ensuring outer elements are split to a place where block elements are allowed to be inserted
12429
12521
  // Also wraps empty clones of split parent tags around fragment to keep formatting
12430
12522
  // If firstOuterBlock is given assume that instead of finding outer (useful for solving cases of some blocks are allowed into others while others are not)
@@ -12432,7 +12524,7 @@ wysihtml.Commands = Base.extend(
12432
12524
  var rangeStartContainer = range.startContainer,
12433
12525
  firstOuterBlock = firstOuterBlock || findOuterBlock(rangeStartContainer, composer.element, true),
12434
12526
  outerInlines, first, last, prev, next;
12435
-
12527
+
12436
12528
  if (firstOuterBlock) {
12437
12529
  // If selection starts inside un-nestable block, split-escape the unnestable point and insert node between
12438
12530
  first = fragment.firstChild;
@@ -12473,7 +12565,7 @@ wysihtml.Commands = Base.extend(
12473
12565
  }
12474
12566
  }
12475
12567
  }
12476
-
12568
+
12477
12569
  // Removes all block formatting from range
12478
12570
  function clearRangeBlockFromating(range, closestBlockName, composer) {
12479
12571
  var r = range.cloneRange(),
@@ -12483,16 +12575,16 @@ wysihtml.Commands = Base.extend(
12483
12575
  fragment = composer.doc.createDocumentFragment(),
12484
12576
  children, blocks,
12485
12577
  first = true;
12486
-
12578
+
12487
12579
  while(content.firstChild) {
12488
12580
  // Iterate over all selection content first level childNodes
12489
12581
  if (content.firstChild.nodeType === 1 && content.firstChild.matches(BLOCK_ELEMENTS)) {
12490
12582
  // If node is a block element
12491
12583
  // Split block formating and add new block to wrap caret
12492
-
12584
+
12493
12585
  unwrapBlocksFromContent(content.firstChild);
12494
12586
  children = wysihtml.dom.unwrap(content.firstChild);
12495
-
12587
+
12496
12588
  // Add line break before if needed
12497
12589
  if (children.length > 0) {
12498
12590
  if (
@@ -12502,11 +12594,11 @@ wysihtml.Commands = Base.extend(
12502
12594
  fragment.appendChild(composer.doc.createElement('BR'));
12503
12595
  }
12504
12596
  }
12505
-
12597
+
12506
12598
  for (var c = 0, cmax = children.length; c < cmax; c++) {
12507
12599
  fragment.appendChild(children[c]);
12508
12600
  }
12509
-
12601
+
12510
12602
  // Add line break after if needed
12511
12603
  if (children.length > 0) {
12512
12604
  if (fragment.lastChild.nodeType !== 1 || !isLineBreaking(fragment.lastChild, composer)) {
@@ -12515,18 +12607,18 @@ wysihtml.Commands = Base.extend(
12515
12607
  }
12516
12608
  }
12517
12609
  }
12518
-
12610
+
12519
12611
  } else {
12520
12612
  fragment.appendChild(content.firstChild);
12521
12613
  }
12522
-
12614
+
12523
12615
  first = false;
12524
12616
  }
12525
12617
  blocks = wysihtml.lang.array(fragment.childNodes).get();
12526
12618
  injectFragmentToRange(fragment, r, composer);
12527
12619
  return blocks;
12528
12620
  }
12529
-
12621
+
12530
12622
  // When block node is inserted, look surrounding nodes and remove surplous linebreak tags (as block format breaks line itself)
12531
12623
  function removeSurroundingLineBreaks(prevNode, nextNode, composer) {
12532
12624
  var prevPrev = prevNode && wysihtml.dom.domNode(prevNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
@@ -12629,25 +12721,25 @@ wysihtml.Commands = Base.extend(
12629
12721
  trimBlankTextsAndBreaks(content);
12630
12722
 
12631
12723
  if (options && options.nodeName === "BLOCKQUOTE") {
12632
-
12724
+
12633
12725
  // If blockquote is to be inserted no quessing just add it as outermost block on line or selection
12634
12726
  var tmpEl = applyOptionsToElement(null, options, composer);
12635
12727
  tmpEl.appendChild(content);
12636
12728
  fragment.appendChild(tmpEl);
12637
12729
  blocks = [tmpEl];
12638
-
12730
+
12639
12731
  } else {
12640
12732
 
12641
12733
  if (!content.firstChild) {
12642
- // IF selection is caret (can happen if line is empty) add format around tag
12734
+ // IF selection is caret (can happen if line is empty) add format around tag
12643
12735
  fragment.appendChild(applyOptionsToElement(null, options, composer));
12644
12736
  } else {
12645
12737
 
12646
12738
  while(content.firstChild) {
12647
12739
  // Iterate over all selection content first level childNodes
12648
-
12740
+
12649
12741
  if (content.firstChild.nodeType == 1 && content.firstChild.matches(BLOCK_ELEMENTS)) {
12650
-
12742
+
12651
12743
  // If node is a block element
12652
12744
  // Escape(split) block formatting at caret
12653
12745
  applyOptionsToElement(content.firstChild, options, composer);
@@ -12655,9 +12747,9 @@ wysihtml.Commands = Base.extend(
12655
12747
  unwrapBlocksFromContent(content.firstChild);
12656
12748
  }
12657
12749
  fragment.appendChild(content.firstChild);
12658
-
12750
+
12659
12751
  } else {
12660
-
12752
+
12661
12753
  // Wrap subsequent non-block nodes inside new block element
12662
12754
  wrapper = applyOptionsToElement(null, getOptionsWithNodename(options, closestBlockName, composer), composer);
12663
12755
  while(content.firstChild && (content.firstChild.nodeType !== 1 || !content.firstChild.matches(BLOCK_ELEMENTS))) {
@@ -12696,7 +12788,7 @@ wysihtml.Commands = Base.extend(
12696
12788
 
12697
12789
  return (parentNode) ? parentNode.nodeName : null;
12698
12790
  }
12699
-
12791
+
12700
12792
  // Expands caret to cover the closest block that:
12701
12793
  // * cannot contain other block level elements (h1-6,p, etc)
12702
12794
  // * Has the same nodeName that is to be inserted
@@ -12718,7 +12810,7 @@ wysihtml.Commands = Base.extend(
12718
12810
  composer.selection.selectLine();
12719
12811
  }
12720
12812
  }
12721
-
12813
+
12722
12814
  // Set selection to begin inside first created block element (beginning of it) and end inside (and after content) of last block element
12723
12815
  // TODO: Checking nodetype might be unnescescary as nodes inserted by formatBlock are nodetype 1 anyway
12724
12816
  function selectElements(newBlockElements, composer) {
@@ -12730,18 +12822,18 @@ wysihtml.Commands = Base.extend(
12730
12822
  range.setEnd(lastEl, lastOffset);
12731
12823
  range.select();
12732
12824
  }
12733
-
12825
+
12734
12826
  // Get all ranges from selection (takes out uneditables and out of editor parts) and apply format to each
12735
- // Return created/modified block level elements
12827
+ // Return created/modified block level elements
12736
12828
  // Method can be either "apply" or "remove"
12737
12829
  function formatSelection(method, composer, options) {
12738
12830
  var ranges = composer.selection.getOwnRanges(),
12739
12831
  newBlockElements = [],
12740
12832
  closestBlockName;
12741
-
12833
+
12742
12834
  // Some places do not allow block level elements inbetween (inside ul and outside li, inside table and outside of td/th)
12743
12835
  ranges = fixNotPermittedInsertionPoints(ranges);
12744
-
12836
+
12745
12837
  for (var i = ranges.length; i--;) {
12746
12838
  fixRangeCoverage(ranges[i], composer);
12747
12839
  closestBlockName = getParentBlockNodeName(ranges[i].startContainer, composer);
@@ -12753,8 +12845,8 @@ wysihtml.Commands = Base.extend(
12753
12845
  }
12754
12846
  return newBlockElements;
12755
12847
  }
12756
-
12757
- // If properties is passed as a string, look for tag with that tagName/query
12848
+
12849
+ // If properties is passed as a string, look for tag with that tagName/query
12758
12850
  function parseOptions(options) {
12759
12851
  if (typeof options === "string") {
12760
12852
  options = {
@@ -12821,13 +12913,13 @@ wysihtml.Commands = Base.extend(
12821
12913
  // Options == null means block formatting should be removed from selection
12822
12914
  newBlockElements = formatSelection("remove", composer);
12823
12915
  }
12824
-
12916
+
12825
12917
  }
12826
12918
 
12827
12919
  // Remove empty block elements that may be left behind
12828
12920
  // Also remove them from new blocks list
12829
12921
  newBlockElements = cleanup(composer, newBlockElements);
12830
-
12922
+
12831
12923
  // Restore selection
12832
12924
  if (bookmark) {
12833
12925
  rangy.restoreSelection(bookmark);
@@ -12835,21 +12927,21 @@ wysihtml.Commands = Base.extend(
12835
12927
  selectElements(newBlockElements, composer);
12836
12928
  }
12837
12929
  },
12838
-
12930
+
12839
12931
  // Removes all block formatting from selection
12840
12932
  remove: function(composer, command, options) {
12841
12933
  options = parseOptions(options);
12842
12934
  var newBlockElements, bookmark;
12843
-
12935
+
12844
12936
  // If selection is caret expand it to cover nearest suitable block element or row if none found
12845
12937
  if (composer.selection.isCollapsed()) {
12846
12938
  bookmark = rangy.saveSelection(composer.win);
12847
12939
  expandCaretToBlock(composer, options && options.nodeName ? options.nodeName.toUpperCase() : undefined);
12848
12940
  }
12849
-
12941
+
12850
12942
  newBlockElements = formatSelection("remove", composer);
12851
12943
  newBlockElements = cleanup(composer, newBlockElements);
12852
-
12944
+
12853
12945
  // Restore selection
12854
12946
  if (bookmark) {
12855
12947
  rangy.restoreSelection(bookmark);
@@ -12989,7 +13081,7 @@ wysihtml.Commands = Base.extend(
12989
13081
  }
12990
13082
 
12991
13083
  // If attrbutes and values are the same > remove
12992
- // if attributes or values
13084
+ // if attributes or values
12993
13085
  function updateElementAttributes(element, newAttributes, toggle) {
12994
13086
  var attr = wysihtml.dom.getAttributes(element),
12995
13087
  fullContain = containsSameAttributes(newAttributes, attr),
@@ -13063,7 +13155,7 @@ wysihtml.Commands = Base.extend(
13063
13155
  // Handle similar semantically same elements (queryAliasMap)
13064
13156
  nodeNameQuery = options.nodeName ? queryAliasMap[options.nodeName.toLowerCase()] || options.nodeName.toLowerCase() : null;
13065
13157
  nodeQueryMatch = nodeNameQuery ? wysihtml.dom.domNode(element).test({ query: nodeNameQuery }) : false;
13066
-
13158
+
13067
13159
  // Unwrap element if no attributes present and node name given
13068
13160
  // or no attributes and if no nodename set but node is the default
13069
13161
  if (!options.nodeName || options.nodeName === defaultTag || nodeQueryMatch) {
@@ -13136,7 +13228,7 @@ wysihtml.Commands = Base.extend(
13136
13228
  selection = rangy.getSelection(composer.win);
13137
13229
 
13138
13230
  rangy.getSelection(composer.win).removeAllRanges();
13139
-
13231
+
13140
13232
  // IE looses focus of contenteditable on removeallranges and can not set new selection unless contenteditable is focused again
13141
13233
  try {
13142
13234
  rangy.getSelection(composer.win).addRange(range);
@@ -13158,7 +13250,7 @@ wysihtml.Commands = Base.extend(
13158
13250
  range.setEnd(lastText, lastText.length);
13159
13251
  selectRange(composer, range);
13160
13252
  }
13161
-
13253
+
13162
13254
  }
13163
13255
 
13164
13256
  function selectTextNode(composer, node, start, end) {
@@ -13210,7 +13302,7 @@ wysihtml.Commands = Base.extend(
13210
13302
  }
13211
13303
 
13212
13304
  }
13213
-
13305
+
13214
13306
  return {
13215
13307
  nodes: nodes,
13216
13308
  partial: partial
@@ -13234,7 +13326,7 @@ wysihtml.Commands = Base.extend(
13234
13326
  }
13235
13327
 
13236
13328
  // Returns a range and textnode containing object from caret position covering a whole word
13237
- // wordOffsety describes the original position of caret in the new textNode
13329
+ // wordOffsety describes the original position of caret in the new textNode
13238
13330
  // Caret has to be inside a textNode.
13239
13331
  function getRangeForWord(selection) {
13240
13332
  var anchor, offset, doc, range, offsetStart, offsetEnd, beforeChar, afterChar,
@@ -13282,7 +13374,7 @@ wysihtml.Commands = Base.extend(
13282
13374
 
13283
13375
  function mergeConsequentSimilarElements(elements) {
13284
13376
  for (var i = elements.length; i--;) {
13285
-
13377
+
13286
13378
  if (elements[i] && elements[i].parentNode) { // Test if node is not allready removed in cleanup
13287
13379
 
13288
13380
  if (elements[i].nextSibling && isSameNode(elements[i], elements[i].nextSibling)) {
@@ -13366,7 +13458,7 @@ wysihtml.Commands = Base.extend(
13366
13458
  if (options.toggle !== false) {
13367
13459
  if (caretIsInsideWord(selection)) {
13368
13460
 
13369
- // Unformat whole word
13461
+ // Unformat whole word
13370
13462
  wordObj = getRangeForWord(selection);
13371
13463
  textNode = wordObj.textNode;
13372
13464
  unformatTextNode(wordObj.textNode, composer, options);
@@ -13408,13 +13500,13 @@ wysihtml.Commands = Base.extend(
13408
13500
  }
13409
13501
 
13410
13502
  } else {
13411
-
13503
+
13412
13504
  // Selection is partially in format
13413
13505
  // change it to new if format if textnode allreafy in similar state
13414
13506
  // else just apply
13415
-
13507
+
13416
13508
  for (i = textNodes.length; i--;) {
13417
-
13509
+
13418
13510
  if (findSimilarTextNodeWrapper(textNodes[i], options, composer.element)) {
13419
13511
  unformatTextNode(textNodes[i], composer, options);
13420
13512
  }
@@ -13435,7 +13527,7 @@ wysihtml.Commands = Base.extend(
13435
13527
  var textNode, textOffset, newNode, i,
13436
13528
  selection = composer.selection.getSelection();
13437
13529
 
13438
- if (!textNodes.length) {
13530
+ if (!textNodes.length) {
13439
13531
  textNode = selection.anchorNode;
13440
13532
  textOffset = selection.anchorOffset;
13441
13533
 
@@ -13456,7 +13548,7 @@ wysihtml.Commands = Base.extend(
13456
13548
  function applyFormat(composer, textNodes, options) {
13457
13549
  var wordObj, i,
13458
13550
  selection = composer.selection.getSelection();
13459
-
13551
+
13460
13552
  if (!textNodes.length) {
13461
13553
  // Handle collapsed selection caret and return
13462
13554
  if (caretIsInsideWord(selection)) {
@@ -13471,7 +13563,7 @@ wysihtml.Commands = Base.extend(
13471
13563
  formatTextRange(r, composer, options);
13472
13564
  }
13473
13565
  }
13474
-
13566
+
13475
13567
  } else {
13476
13568
  // Handle textnodes in selection and apply format
13477
13569
  for (i = textNodes.length; i--;) {
@@ -13480,7 +13572,7 @@ wysihtml.Commands = Base.extend(
13480
13572
  cleanupAndSetSelection(composer, textNodes, options);
13481
13573
  }
13482
13574
  }
13483
-
13575
+
13484
13576
  // If properties is passed as a string, correct options with that nodeName
13485
13577
  function fixOptions(options) {
13486
13578
  options = (typeof options === "string") ? { nodeName: options } : options;
@@ -13523,7 +13615,7 @@ wysihtml.Commands = Base.extend(
13523
13615
  // Text allready has the format applied
13524
13616
  removeFormat(composer, textNodes, state, options);
13525
13617
  }
13526
-
13618
+
13527
13619
  composer.element.normalize();
13528
13620
  },
13529
13621
 
@@ -14849,7 +14941,7 @@ wysihtml.views.View = Base.extend(
14849
14941
 
14850
14942
  // --------- restore focus ---------
14851
14943
  if (originalActiveElement) {
14852
- originalActiveElement.focus();
14944
+ focusWithoutScrolling(originalActiveElement);
14853
14945
  } else {
14854
14946
  textareaElement.blur();
14855
14947
  }
@@ -14925,7 +15017,7 @@ wysihtml.views.View = Base.extend(
14925
15017
 
14926
15018
  // Override for giving user ability to delete last line break in table cell
14927
15019
  fixLastBrDeletionInTable: function(composer, force) {
14928
- if (composer.selection.caretIsLastInSelection()) {
15020
+ if (composer.selection.caretIsInTheEndOfNode()) {
14929
15021
  var sel = composer.selection.getSelection(),
14930
15022
  aNode = sel.anchorNode;
14931
15023
  if (aNode && aNode.nodeType === 1 && (wysihtml.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
@@ -15191,6 +15283,18 @@ wysihtml.views.View = Base.extend(
15191
15283
  }
15192
15284
  }
15193
15285
  }
15286
+
15287
+ if (browser.hasCaretAtLinkEndInsertionProblems() && composer.selection.caretIsInTheEndOfNode()) {
15288
+ var target = composer.selection.getSelectedNode(true),
15289
+ targetEl = (target && target.nodeType === 3) ? target.parentNode : target, // target guaranteed to be an Element
15290
+ invisibleSpace, space;
15291
+
15292
+ if (targetEl && targetEl.closest('a') && target.nodeType === 3 && target === targetEl.lastChild) {
15293
+ // Seems like enter was pressed and caret was at the end of link node
15294
+ // This means user wants to escape the link now (caret is last in link node too).
15295
+ composer.selection.setAfter(targetEl);
15296
+ }
15297
+ }
15194
15298
  };
15195
15299
 
15196
15300
  var handleTabKeyDown = function(composer, element, shiftKey) {
@@ -15335,7 +15439,9 @@ wysihtml.views.View = Base.extend(
15335
15439
  var handleKeyDown = function(event) {
15336
15440
  var keyCode = event.keyCode,
15337
15441
  command = shortcuts[keyCode],
15338
- target, parent;
15442
+ target = this.selection.getSelectedNode(true),
15443
+ targetEl = (target && target.nodeType === 3) ? target.parentNode : target, // target guaranteed to be an Element
15444
+ parent;
15339
15445
 
15340
15446
  // Select all (meta/ctrl + a)
15341
15447
  if ((event.ctrlKey || event.metaKey) && !event.altKey && keyCode === 65) {
@@ -15357,7 +15463,6 @@ wysihtml.views.View = Base.extend(
15357
15463
 
15358
15464
  // Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor
15359
15465
  if (keyCode === wysihtml.BACKSPACE_KEY || keyCode === wysihtml.DELETE_KEY) {
15360
- target = this.selection.getSelectedNode(true);
15361
15466
  if (target && target.nodeName === "IMG") {
15362
15467
  event.preventDefault();
15363
15468
  parent = target.parentNode;
@@ -15384,6 +15489,64 @@ wysihtml.views.View = Base.extend(
15384
15489
 
15385
15490
  };
15386
15491
 
15492
+ var handleKeyPress = function(event) {
15493
+
15494
+ // This block should run only if some character is inserted (nor command keys like delete, backspace, enter, etc.)
15495
+ if (event.which !== 0) {
15496
+
15497
+ // Test if caret is last in a link in webkit and try to fix webkit problem,
15498
+ // that all inserted content is added outside of link.
15499
+ // This issue was added as a not thought through fix for getting caret after link in contenteditable if it is last in editable area.
15500
+ // Allthough it fixes this minor case it actually introduces a cascade of problems when editing links.
15501
+ // The standard approachi in other wysiwygs seems as a step backwards - introducing a separate modal for managing links content text.
15502
+ // I find it to be too big of a tradeoff in terms of expected simple UI flow, thus trying to fight against it.
15503
+ // Also adds link escaping by double space with caret at the end of link for all browsers
15504
+
15505
+ if (this.selection.caretIsInTheEndOfNode()) {
15506
+ var target = this.selection.getSelectedNode(true),
15507
+ targetEl = (target && target.nodeType === 3) ? target.parentNode : target, // target guaranteed to be an Element
15508
+ invisibleSpace, space;
15509
+
15510
+ if (targetEl && targetEl.closest('a') && target === targetEl.lastChild) {
15511
+
15512
+ if (event.which !== 32 || this.selection.caretIsInTheEndOfNode(true) && browser.hasCaretAtLinkEndInsertionProblems()) {
15513
+ // Executed if there is no whitespace before caret in textnode in case of pressing space.
15514
+ // Whitespace before marks that user wants to escape the node by pressing double space.
15515
+ // Otherwise insert the character in the link not out as it would like to go natively
15516
+
15517
+ invisibleSpace = this.doc.createTextNode(wysihtml.INVISIBLE_SPACE);
15518
+ this.selection.insertNode(invisibleSpace);
15519
+ this.selection.setBefore(invisibleSpace);
15520
+ setTimeout(function() {
15521
+
15522
+ if (invisibleSpace.textContent.length > 1) {
15523
+ invisibleSpace.textContent = invisibleSpace.textContent.replace(wysihtml.INVISIBLE_SPACE_REG_EXP, '');
15524
+ this.selection.setAfter(invisibleSpace);
15525
+ } else {
15526
+ invisibleSpace.remove();
15527
+ }
15528
+
15529
+ }.bind(this), 0);
15530
+ } else if (event.which === 32) {
15531
+ // Seems like space was pressed and there was a space before the caret allready
15532
+ // This means user wants to escape the link now (caret is last in link node too) so we let the native browser do it-s job and escape.
15533
+ // But lets move the trailing space too out of link if present
15534
+
15535
+ if (target.nodeType === 3 && (/[\u00A0 ]$/).test(target.textContent)) {
15536
+
15537
+ target.textContent = target.textContent.replace(/[\u00A0 ]$/, '');
15538
+ space = this.doc.createTextNode(' ');
15539
+ targetEl.parentNode.insertBefore(space, targetEl.nextSibling);
15540
+ this.selection.setAfter(space, false);
15541
+ event.preventDefault();
15542
+
15543
+ }
15544
+ }
15545
+ }
15546
+ }
15547
+ }
15548
+ }
15549
+
15387
15550
  var handleIframeFocus = function(event) {
15388
15551
  setTimeout((function() {
15389
15552
  if (this.doc.querySelector(":focus") !== this.element) {
@@ -15408,6 +15571,7 @@ wysihtml.views.View = Base.extend(
15408
15571
  focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? this.element : this.sandbox.getWindow();
15409
15572
 
15410
15573
  this.focusState = this.getValue(false, false);
15574
+ this.actions = actions;
15411
15575
 
15412
15576
  // --------- destroy:composer event ---------
15413
15577
  container.addEventListener(["DOMNodeRemoved"], handleDomNodeRemoved.bind(this), false);
@@ -15422,17 +15586,18 @@ wysihtml.views.View = Base.extend(
15422
15586
  }, 250);
15423
15587
  }
15424
15588
 
15425
- actions.addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
15426
- focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
15427
- focusBlurElement.addEventListener("blur", handleBlur.bind(this), false);
15589
+ actions.addListeners(focusBlurElement, ['drop', 'paste', 'mouseup', 'focus', 'keyup'], handleUserInteraction.bind(this));
15590
+ focusBlurElement.addEventListener('focus', handleFocus.bind(this), false);
15591
+ focusBlurElement.addEventListener('blur', handleBlur.bind(this), false);
15428
15592
 
15429
- actions.addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
15430
- this.element.addEventListener("copy", handleCopy.bind(this), false);
15431
- this.element.addEventListener("mousedown", handleMouseDown.bind(this), false);
15432
- this.element.addEventListener("click", handleClick.bind(this), false);
15433
- this.element.addEventListener("drop", handleDrop.bind(this), false);
15434
- this.element.addEventListener("keyup", handleKeyUp.bind(this), false);
15435
- this.element.addEventListener("keydown", handleKeyDown.bind(this), false);
15593
+ actions.addListeners(this.element, ['drop', 'paste', 'beforepaste'], handlePaste.bind(this), false);
15594
+ this.element.addEventListener('copy', handleCopy.bind(this), false);
15595
+ this.element.addEventListener('mousedown', handleMouseDown.bind(this), false);
15596
+ this.element.addEventListener('click', handleClick.bind(this), false);
15597
+ this.element.addEventListener('drop', handleDrop.bind(this), false);
15598
+ this.element.addEventListener('keyup', handleKeyUp.bind(this), false);
15599
+ this.element.addEventListener('keydown', handleKeyDown.bind(this), false);
15600
+ this.element.addEventListener('keypress', handleKeyPress.bind(this), false);
15436
15601
 
15437
15602
  // IE controlselect madness fix
15438
15603
  if (wysihtml.browser.usesControlRanges()) {
@@ -15752,12 +15917,12 @@ wysihtml.views.Textarea = wysihtml.views.View.extend(
15752
15917
  uneditableContainer: "wysihtml-uneditable-container"
15753
15918
  },
15754
15919
  // Browsers that support copied source handling will get a marking of the origin of the copied source (for determinig code cleanup rules on paste)
15755
- // Also copied source is based directly on selection -
15920
+ // Also copied source is based directly on selection -
15756
15921
  // (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).
15757
15922
  // If falsy value is passed source override is also disabled
15758
15923
  copyedFromMarking: '<meta name="copied-from" content="wysihtml">'
15759
15924
  },
15760
-
15925
+
15761
15926
  constructor: function(editableElement, config) {
15762
15927
  this.editableElement = typeof(editableElement) === "string" ? document.getElementById(editableElement) : editableElement;
15763
15928
  this.config = wysihtml.lang.object({}).merge(this.defaults).merge(config).get();
@@ -15805,7 +15970,7 @@ wysihtml.views.Textarea = wysihtml.views.View.extend(
15805
15970
  }
15806
15971
  this.runEditorExtenders();
15807
15972
  },
15808
-
15973
+
15809
15974
  runEditorExtenders: function() {
15810
15975
  wysihtml.editorExtenders.forEach(function(extender) {
15811
15976
  extender(this);