wysihtml5x-rails 0.4.16 → 0.4.17
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/lib/wysihtml5x/rails/version.rb +1 -1
- data/vendor/assets/javascripts/wysihtml5x-toolbar.js +611 -337
- data/vendor/assets/javascripts/wysihtml5x.js +611 -337
- metadata +2 -2
@@ -1,8 +1,84 @@
|
|
1
1
|
// TODO: in future try to replace most inline compability checks with polyfills for code readability
|
2
2
|
|
3
|
-
//
|
4
|
-
//
|
3
|
+
// IE8 SUPPORT BLOCK
|
4
|
+
// You can compile wuthout all this if IE8 is not needed
|
5
|
+
|
6
|
+
// addEventListener, removeEventListener
|
7
|
+
// TODO: make usage of wysihtml5.dom.observe obsolete
|
8
|
+
(function() {
|
9
|
+
if (!Event.prototype.preventDefault) {
|
10
|
+
Event.prototype.preventDefault=function() {
|
11
|
+
this.returnValue=false;
|
12
|
+
};
|
13
|
+
}
|
14
|
+
if (!Event.prototype.stopPropagation) {
|
15
|
+
Event.prototype.stopPropagation=function() {
|
16
|
+
this.cancelBubble=true;
|
17
|
+
};
|
18
|
+
}
|
19
|
+
if (!Element.prototype.addEventListener) {
|
20
|
+
var eventListeners=[];
|
21
|
+
|
22
|
+
var addEventListener=function(type,listener /*, useCapture (will be ignored) */) {
|
23
|
+
var self=this;
|
24
|
+
var wrapper=function(e) {
|
25
|
+
e.target=e.srcElement;
|
26
|
+
e.currentTarget=self;
|
27
|
+
if (listener.handleEvent) {
|
28
|
+
listener.handleEvent(e);
|
29
|
+
} else {
|
30
|
+
listener.call(self,e);
|
31
|
+
}
|
32
|
+
};
|
33
|
+
if (type=="DOMContentLoaded") {
|
34
|
+
var wrapper2=function(e) {
|
35
|
+
if (document.readyState=="complete") {
|
36
|
+
wrapper(e);
|
37
|
+
}
|
38
|
+
};
|
39
|
+
document.attachEvent("onreadystatechange",wrapper2);
|
40
|
+
eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2});
|
41
|
+
|
42
|
+
if (document.readyState=="complete") {
|
43
|
+
var e=new Event();
|
44
|
+
e.srcElement=window;
|
45
|
+
wrapper2(e);
|
46
|
+
}
|
47
|
+
} else {
|
48
|
+
this.attachEvent("on"+type,wrapper);
|
49
|
+
eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper});
|
50
|
+
}
|
51
|
+
};
|
52
|
+
var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) {
|
53
|
+
var counter=0;
|
54
|
+
while (counter<eventListeners.length) {
|
55
|
+
var eventListener=eventListeners[counter];
|
56
|
+
if (eventListener.object==this && eventListener.type==type && eventListener.listener==listener) {
|
57
|
+
if (type=="DOMContentLoaded") {
|
58
|
+
this.detachEvent("onreadystatechange",eventListener.wrapper);
|
59
|
+
} else {
|
60
|
+
this.detachEvent("on"+type,eventListener.wrapper);
|
61
|
+
}
|
62
|
+
eventListeners.splice(counter, 1);
|
63
|
+
break;
|
64
|
+
}
|
65
|
+
++counter;
|
66
|
+
}
|
67
|
+
};
|
68
|
+
Element.prototype.addEventListener=addEventListener;
|
69
|
+
Element.prototype.removeEventListener=removeEventListener;
|
70
|
+
if (HTMLDocument) {
|
71
|
+
HTMLDocument.prototype.addEventListener=addEventListener;
|
72
|
+
HTMLDocument.prototype.removeEventListener=removeEventListener;
|
73
|
+
}
|
74
|
+
if (Window) {
|
75
|
+
Window.prototype.addEventListener=addEventListener;
|
76
|
+
Window.prototype.removeEventListener=removeEventListener;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
})();
|
5
80
|
|
81
|
+
// element.textContent polyfill.
|
6
82
|
if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get) {
|
7
83
|
(function() {
|
8
84
|
var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
|
@@ -24,8 +100,35 @@ if(!Array.isArray) {
|
|
24
100
|
Array.isArray = function(arg) {
|
25
101
|
return Object.prototype.toString.call(arg) === '[object Array]';
|
26
102
|
};
|
103
|
+
}
|
104
|
+
|
105
|
+
// Function.prototype.bind()
|
106
|
+
// TODO: clean the code from variable 'that' as it can be confusing
|
107
|
+
if (!Function.prototype.bind) {
|
108
|
+
Function.prototype.bind = function(oThis) {
|
109
|
+
if (typeof this !== 'function') {
|
110
|
+
// closest thing possible to the ECMAScript 5
|
111
|
+
// internal IsCallable function
|
112
|
+
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
113
|
+
}
|
114
|
+
|
115
|
+
var aArgs = Array.prototype.slice.call(arguments, 1),
|
116
|
+
fToBind = this,
|
117
|
+
fNOP = function() {},
|
118
|
+
fBound = function() {
|
119
|
+
return fToBind.apply(this instanceof fNOP && oThis
|
120
|
+
? this
|
121
|
+
: oThis,
|
122
|
+
aArgs.concat(Array.prototype.slice.call(arguments)));
|
123
|
+
};
|
124
|
+
|
125
|
+
fNOP.prototype = this.prototype;
|
126
|
+
fBound.prototype = new fNOP();
|
127
|
+
|
128
|
+
return fBound;
|
129
|
+
};
|
27
130
|
};/**
|
28
|
-
* @license wysihtml5x v0.4.
|
131
|
+
* @license wysihtml5x v0.4.17
|
29
132
|
* https://github.com/Edicy/wysihtml5
|
30
133
|
*
|
31
134
|
* Author: Christopher Blum (https://github.com/tiff)
|
@@ -36,7 +139,7 @@ if(!Array.isArray) {
|
|
36
139
|
*
|
37
140
|
*/
|
38
141
|
var wysihtml5 = {
|
39
|
-
version: "0.4.
|
142
|
+
version: "0.4.17",
|
40
143
|
|
41
144
|
// namespaces
|
42
145
|
commands: {},
|
@@ -48,6 +151,7 @@ var wysihtml5 = {
|
|
48
151
|
views: {},
|
49
152
|
|
50
153
|
INVISIBLE_SPACE: "\uFEFF",
|
154
|
+
INVISIBLE_SPACE_REG_EXP: /\uFEFF/g,
|
51
155
|
|
52
156
|
EMPTY_FUNCTION: function() {},
|
53
157
|
|
@@ -58,32 +162,29 @@ var wysihtml5 = {
|
|
58
162
|
ENTER_KEY: 13,
|
59
163
|
ESCAPE_KEY: 27,
|
60
164
|
SPACE_KEY: 32,
|
165
|
+
TAB_KEY: 9,
|
61
166
|
DELETE_KEY: 46
|
62
167
|
};
|
63
168
|
;/**
|
64
169
|
* Rangy, a cross-browser JavaScript range and selection library
|
65
|
-
*
|
170
|
+
* https://github.com/timdown/rangy
|
66
171
|
*
|
67
172
|
* Copyright 2014, Tim Down
|
68
173
|
* Licensed under the MIT license.
|
69
|
-
* Version: 1.
|
70
|
-
* Build date:
|
174
|
+
* Version: 1.3.0-alpha.20140921
|
175
|
+
* Build date: 21 September 2014
|
71
176
|
*/
|
72
177
|
|
73
|
-
(function(factory,
|
178
|
+
(function(factory, root) {
|
74
179
|
if (typeof define == "function" && define.amd) {
|
75
180
|
// AMD. Register as an anonymous module.
|
76
181
|
define(factory);
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
} else if (typeof exports == "object") {
|
81
|
-
// Node/CommonJS style for Browserify
|
82
|
-
module.exports = factory;
|
83
|
-
*/
|
182
|
+
} else if (typeof module != "undefined" && typeof exports == "object") {
|
183
|
+
// Node/CommonJS style
|
184
|
+
module.exports = factory();
|
84
185
|
} else {
|
85
|
-
// No AMD or CommonJS support so we place Rangy in
|
86
|
-
|
186
|
+
// No AMD or CommonJS support so we place Rangy in (probably) the global variable
|
187
|
+
root.rangy = factory();
|
87
188
|
}
|
88
189
|
})(function() {
|
89
190
|
|
@@ -150,24 +251,26 @@ var wysihtml5 = {
|
|
150
251
|
|
151
252
|
var modules = {};
|
152
253
|
|
254
|
+
var isBrowser = (typeof window != UNDEFINED && typeof document != UNDEFINED);
|
255
|
+
|
256
|
+
var util = {
|
257
|
+
isHostMethod: isHostMethod,
|
258
|
+
isHostObject: isHostObject,
|
259
|
+
isHostProperty: isHostProperty,
|
260
|
+
areHostMethods: areHostMethods,
|
261
|
+
areHostObjects: areHostObjects,
|
262
|
+
areHostProperties: areHostProperties,
|
263
|
+
isTextRange: isTextRange,
|
264
|
+
getBody: getBody
|
265
|
+
};
|
266
|
+
|
153
267
|
var api = {
|
154
|
-
version: "1.
|
268
|
+
version: "1.3.0-alpha.20140921",
|
155
269
|
initialized: false,
|
270
|
+
isBrowser: isBrowser,
|
156
271
|
supported: true,
|
157
|
-
|
158
|
-
util: {
|
159
|
-
isHostMethod: isHostMethod,
|
160
|
-
isHostObject: isHostObject,
|
161
|
-
isHostProperty: isHostProperty,
|
162
|
-
areHostMethods: areHostMethods,
|
163
|
-
areHostObjects: areHostObjects,
|
164
|
-
areHostProperties: areHostProperties,
|
165
|
-
isTextRange: isTextRange,
|
166
|
-
getBody: getBody
|
167
|
-
},
|
168
|
-
|
272
|
+
util: util,
|
169
273
|
features: {},
|
170
|
-
|
171
274
|
modules: modules,
|
172
275
|
config: {
|
173
276
|
alertOnFail: true,
|
@@ -178,14 +281,14 @@ var wysihtml5 = {
|
|
178
281
|
};
|
179
282
|
|
180
283
|
function consoleLog(msg) {
|
181
|
-
if (
|
182
|
-
|
284
|
+
if (typeof console != UNDEFINED && isHostMethod(console, "log")) {
|
285
|
+
console.log(msg);
|
183
286
|
}
|
184
287
|
}
|
185
288
|
|
186
289
|
function alertOrLog(msg, shouldAlert) {
|
187
|
-
if (shouldAlert) {
|
188
|
-
|
290
|
+
if (isBrowser && shouldAlert) {
|
291
|
+
alert(msg);
|
189
292
|
} else {
|
190
293
|
consoleLog(msg);
|
191
294
|
}
|
@@ -194,7 +297,7 @@ var wysihtml5 = {
|
|
194
297
|
function fail(reason) {
|
195
298
|
api.initialized = true;
|
196
299
|
api.supported = false;
|
197
|
-
alertOrLog("Rangy is not supported
|
300
|
+
alertOrLog("Rangy is not supported in this environment. Reason: " + reason, api.config.alertOnFail);
|
198
301
|
}
|
199
302
|
|
200
303
|
api.fail = fail;
|
@@ -206,15 +309,16 @@ var wysihtml5 = {
|
|
206
309
|
api.warn = warn;
|
207
310
|
|
208
311
|
// Add utility extend() method
|
312
|
+
var extend;
|
209
313
|
if ({}.hasOwnProperty) {
|
210
|
-
|
314
|
+
util.extend = extend = function(obj, props, deep) {
|
211
315
|
var o, p;
|
212
316
|
for (var i in props) {
|
213
317
|
if (props.hasOwnProperty(i)) {
|
214
318
|
o = obj[i];
|
215
319
|
p = props[i];
|
216
320
|
if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
|
217
|
-
|
321
|
+
extend(o, p, true);
|
218
322
|
}
|
219
323
|
obj[i] = p;
|
220
324
|
}
|
@@ -225,23 +329,40 @@ var wysihtml5 = {
|
|
225
329
|
}
|
226
330
|
return obj;
|
227
331
|
};
|
332
|
+
|
333
|
+
util.createOptions = function(optionsParam, defaults) {
|
334
|
+
var options = {};
|
335
|
+
extend(options, defaults);
|
336
|
+
if (optionsParam) {
|
337
|
+
extend(options, optionsParam);
|
338
|
+
}
|
339
|
+
return options;
|
340
|
+
};
|
228
341
|
} else {
|
229
342
|
fail("hasOwnProperty not supported");
|
230
343
|
}
|
344
|
+
|
345
|
+
// Test whether we're in a browser and bail out if not
|
346
|
+
if (!isBrowser) {
|
347
|
+
fail("Rangy can only run in a browser");
|
348
|
+
}
|
231
349
|
|
232
350
|
// Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
|
233
351
|
(function() {
|
234
|
-
var el = document.createElement("div");
|
235
|
-
el.appendChild(document.createElement("span"));
|
236
|
-
var slice = [].slice;
|
237
352
|
var toArray;
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
353
|
+
|
354
|
+
if (isBrowser) {
|
355
|
+
var el = document.createElement("div");
|
356
|
+
el.appendChild(document.createElement("span"));
|
357
|
+
var slice = [].slice;
|
358
|
+
try {
|
359
|
+
if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
|
360
|
+
toArray = function(arrayLike) {
|
361
|
+
return slice.call(arrayLike, 0);
|
362
|
+
};
|
363
|
+
}
|
364
|
+
} catch (e) {}
|
365
|
+
}
|
245
366
|
|
246
367
|
if (!toArray) {
|
247
368
|
toArray = function(arrayLike) {
|
@@ -253,26 +374,27 @@ var wysihtml5 = {
|
|
253
374
|
};
|
254
375
|
}
|
255
376
|
|
256
|
-
|
377
|
+
util.toArray = toArray;
|
257
378
|
})();
|
258
379
|
|
259
|
-
|
260
380
|
// Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
|
261
381
|
// normalization of event properties
|
262
382
|
var addListener;
|
263
|
-
if (
|
264
|
-
|
265
|
-
obj
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
obj
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
383
|
+
if (isBrowser) {
|
384
|
+
if (isHostMethod(document, "addEventListener")) {
|
385
|
+
addListener = function(obj, eventType, listener) {
|
386
|
+
obj.addEventListener(eventType, listener, false);
|
387
|
+
};
|
388
|
+
} else if (isHostMethod(document, "attachEvent")) {
|
389
|
+
addListener = function(obj, eventType, listener) {
|
390
|
+
obj.attachEvent("on" + eventType, listener);
|
391
|
+
};
|
392
|
+
} else {
|
393
|
+
fail("Document does not have required addEventListener or attachEvent method");
|
394
|
+
}
|
274
395
|
|
275
|
-
|
396
|
+
util.addListener = addListener;
|
397
|
+
}
|
276
398
|
|
277
399
|
var initListeners = [];
|
278
400
|
|
@@ -282,7 +404,7 @@ var wysihtml5 = {
|
|
282
404
|
|
283
405
|
// Initialization
|
284
406
|
function init() {
|
285
|
-
if (api.initialized) {
|
407
|
+
if (!isBrowser || api.initialized) {
|
286
408
|
return;
|
287
409
|
}
|
288
410
|
var testRange;
|
@@ -368,7 +490,9 @@ var wysihtml5 = {
|
|
368
490
|
}
|
369
491
|
}
|
370
492
|
|
371
|
-
|
493
|
+
if (isBrowser) {
|
494
|
+
api.shim = api.createMissingNativeApi = shim;
|
495
|
+
}
|
372
496
|
|
373
497
|
function Module(name, dependencies, initializer) {
|
374
498
|
this.name = name;
|
@@ -420,7 +544,7 @@ var wysihtml5 = {
|
|
420
544
|
}
|
421
545
|
};
|
422
546
|
|
423
|
-
function createModule(
|
547
|
+
function createModule(name, dependencies, initFunc) {
|
424
548
|
var newModule = new Module(name, dependencies, function(module) {
|
425
549
|
if (!module.initialized) {
|
426
550
|
module.initialized = true;
|
@@ -430,10 +554,14 @@ var wysihtml5 = {
|
|
430
554
|
} catch (ex) {
|
431
555
|
var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
|
432
556
|
consoleLog(errorMessage);
|
557
|
+
if (ex.stack) {
|
558
|
+
consoleLog(ex.stack);
|
559
|
+
}
|
433
560
|
}
|
434
561
|
}
|
435
562
|
});
|
436
563
|
modules[name] = newModule;
|
564
|
+
return newModule;
|
437
565
|
}
|
438
566
|
|
439
567
|
api.createModule = function(name) {
|
@@ -447,16 +575,16 @@ var wysihtml5 = {
|
|
447
575
|
dependencies = arguments[1];
|
448
576
|
}
|
449
577
|
|
450
|
-
var module = createModule(
|
578
|
+
var module = createModule(name, dependencies, initFunc);
|
451
579
|
|
452
580
|
// Initialize the module immediately if the core is already initialized
|
453
|
-
if (api.initialized) {
|
581
|
+
if (api.initialized && api.supported) {
|
454
582
|
module.init();
|
455
583
|
}
|
456
584
|
};
|
457
585
|
|
458
586
|
api.createCoreModule = function(name, dependencies, initFunc) {
|
459
|
-
createModule(
|
587
|
+
createModule(name, dependencies, initFunc);
|
460
588
|
};
|
461
589
|
|
462
590
|
/*----------------------------------------------------------------------------------------------------------------*/
|
@@ -472,38 +600,6 @@ var wysihtml5 = {
|
|
472
600
|
|
473
601
|
/*----------------------------------------------------------------------------------------------------------------*/
|
474
602
|
|
475
|
-
// Wait for document to load before running tests
|
476
|
-
|
477
|
-
var docReady = false;
|
478
|
-
|
479
|
-
var loadHandler = function(e) {
|
480
|
-
if (!docReady) {
|
481
|
-
docReady = true;
|
482
|
-
if (!api.initialized && api.config.autoInitialize) {
|
483
|
-
init();
|
484
|
-
}
|
485
|
-
}
|
486
|
-
};
|
487
|
-
|
488
|
-
// Test whether we have window and document objects that we will need
|
489
|
-
if (typeof window == UNDEFINED) {
|
490
|
-
fail("No window found");
|
491
|
-
return;
|
492
|
-
}
|
493
|
-
if (typeof document == UNDEFINED) {
|
494
|
-
fail("No document found");
|
495
|
-
return;
|
496
|
-
}
|
497
|
-
|
498
|
-
if (isHostMethod(document, "addEventListener")) {
|
499
|
-
document.addEventListener("DOMContentLoaded", loadHandler, false);
|
500
|
-
}
|
501
|
-
|
502
|
-
// Add a fallback in case the DOMContentLoaded event isn't supported
|
503
|
-
addListener(window, "load", loadHandler);
|
504
|
-
|
505
|
-
/*----------------------------------------------------------------------------------------------------------------*/
|
506
|
-
|
507
603
|
// DOM utility methods used by Rangy
|
508
604
|
api.createCoreModule("DomUtil", [], function(api, module) {
|
509
605
|
var UNDEF = "undefined";
|
@@ -2385,7 +2481,7 @@ var wysihtml5 = {
|
|
2385
2481
|
|
2386
2482
|
/*--------------------------------------------------------------------------------------------------------*/
|
2387
2483
|
|
2388
|
-
// Test for IE
|
2484
|
+
// Test for IE deleteContents() and extractContents() bug and correct it. See issue 107.
|
2389
2485
|
|
2390
2486
|
var el = document.createElement("div");
|
2391
2487
|
el.innerHTML = "123";
|
@@ -2743,7 +2839,7 @@ var wysihtml5 = {
|
|
2743
2839
|
// implementation to use by default.
|
2744
2840
|
if (!api.features.implementsDomRange || api.config.preferTextRange) {
|
2745
2841
|
// Add WrappedTextRange as the Range property of the global object to allow expression like Range.END_TO_END to work
|
2746
|
-
var globalObj = (function() { return this; })();
|
2842
|
+
var globalObj = (function(f) { return f("return this;")(); })(Function);
|
2747
2843
|
if (typeof globalObj.Range == "undefined") {
|
2748
2844
|
globalObj.Range = WrappedTextRange;
|
2749
2845
|
}
|
@@ -3279,7 +3375,11 @@ var wysihtml5 = {
|
|
3279
3375
|
// Clone the native range so that changing the selected range does not affect the selection.
|
3280
3376
|
// This is contrary to the spec but is the only way to achieve consistency between browsers. See
|
3281
3377
|
// issue 80.
|
3282
|
-
|
3378
|
+
var clonedNativeRange = getNativeRange(range).cloneRange();
|
3379
|
+
try {
|
3380
|
+
this.nativeSelection.addRange(clonedNativeRange);
|
3381
|
+
} catch (ex) {
|
3382
|
+
}
|
3283
3383
|
|
3284
3384
|
// Check whether adding the range was successful
|
3285
3385
|
this.rangeCount = this.nativeSelection.rangeCount;
|
@@ -3792,33 +3892,57 @@ var wysihtml5 = {
|
|
3792
3892
|
|
3793
3893
|
/*----------------------------------------------------------------------------------------------------------------*/
|
3794
3894
|
|
3895
|
+
// Wait for document to load before initializing
|
3896
|
+
var docReady = false;
|
3897
|
+
|
3898
|
+
var loadHandler = function(e) {
|
3899
|
+
if (!docReady) {
|
3900
|
+
docReady = true;
|
3901
|
+
if (!api.initialized && api.config.autoInitialize) {
|
3902
|
+
init();
|
3903
|
+
}
|
3904
|
+
}
|
3905
|
+
};
|
3906
|
+
|
3907
|
+
if (isBrowser) {
|
3908
|
+
// Test whether the document has already been loaded and initialize immediately if so
|
3909
|
+
if (document.readyState == "complete") {
|
3910
|
+
loadHandler();
|
3911
|
+
} else {
|
3912
|
+
if (isHostMethod(document, "addEventListener")) {
|
3913
|
+
document.addEventListener("DOMContentLoaded", loadHandler, false);
|
3914
|
+
}
|
3915
|
+
|
3916
|
+
// Add a fallback in case the DOMContentLoaded event isn't supported
|
3917
|
+
addListener(window, "load", loadHandler);
|
3918
|
+
}
|
3919
|
+
}
|
3920
|
+
|
3795
3921
|
return api;
|
3796
3922
|
}, this);;/**
|
3797
3923
|
* Selection save and restore module for Rangy.
|
3798
3924
|
* Saves and restores user selections using marker invisible elements in the DOM.
|
3799
3925
|
*
|
3800
3926
|
* Part of Rangy, a cross-browser JavaScript range and selection library
|
3801
|
-
*
|
3927
|
+
* https://github.com/timdown/rangy
|
3802
3928
|
*
|
3803
3929
|
* Depends on Rangy core.
|
3804
3930
|
*
|
3805
3931
|
* Copyright 2014, Tim Down
|
3806
3932
|
* Licensed under the MIT license.
|
3807
|
-
* Version: 1.
|
3808
|
-
* Build date:
|
3933
|
+
* Version: 1.3.0-alpha.20140921
|
3934
|
+
* Build date: 21 September 2014
|
3809
3935
|
*/
|
3810
|
-
(function(factory,
|
3936
|
+
(function(factory, root) {
|
3811
3937
|
if (typeof define == "function" && define.amd) {
|
3812
3938
|
// AMD. Register as an anonymous module with a dependency on Rangy.
|
3813
|
-
define(["rangy"], factory);
|
3814
|
-
|
3815
|
-
|
3816
|
-
|
3817
|
-
module.exports = factory;
|
3818
|
-
*/
|
3939
|
+
define(["./rangy-core"], factory);
|
3940
|
+
} else if (typeof module != "undefined" && typeof exports == "object") {
|
3941
|
+
// Node/CommonJS style
|
3942
|
+
module.exports = factory( require("rangy") );
|
3819
3943
|
} else {
|
3820
|
-
// No AMD or CommonJS support so we use the rangy global variable
|
3821
|
-
factory(
|
3944
|
+
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
|
3945
|
+
factory(root.rangy);
|
3822
3946
|
}
|
3823
3947
|
})(function(rangy) {
|
3824
3948
|
rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
|
@@ -4302,13 +4426,6 @@ wysihtml5.browser = (function() {
|
|
4302
4426
|
return "currentStyle" in testElement;
|
4303
4427
|
},
|
4304
4428
|
|
4305
|
-
/**
|
4306
|
-
* Firefox on OSX navigates through history when hitting CMD + Arrow right/left
|
4307
|
-
*/
|
4308
|
-
hasHistoryIssue: function() {
|
4309
|
-
return isGecko && navigator.platform.substr(0, 3) === "Mac";
|
4310
|
-
},
|
4311
|
-
|
4312
4429
|
/**
|
4313
4430
|
* Whether the browser inserts a <br> when pressing enter in a contentEditable element
|
4314
4431
|
*/
|
@@ -5911,7 +6028,10 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
|
|
5911
6028
|
DEFAULT_NODE_NAME = "span",
|
5912
6029
|
WHITE_SPACE_REG_EXP = /\s+/,
|
5913
6030
|
defaultRules = { tags: {}, classes: {} },
|
5914
|
-
currentRules = {}
|
6031
|
+
currentRules = {},
|
6032
|
+
blockElements = ["ADDRESS" ,"BLOCKQUOTE" ,"CENTER" ,"DIR" ,"DIV" ,"DL" ,"FIELDSET" ,
|
6033
|
+
"FORM", "H1" ,"H2" ,"H3" ,"H4" ,"H5" ,"H6" ,"ISINDEX" ,"MENU",
|
6034
|
+
"NOFRAMES", "NOSCRIPT" ,"OL" ,"P" ,"PRE","TABLE", "UL"];
|
5915
6035
|
|
5916
6036
|
/**
|
5917
6037
|
* Iterates over all childs of the element, recreates them, appends them into a document fragment
|
@@ -5978,7 +6098,8 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
|
|
5978
6098
|
i = 0,
|
5979
6099
|
fragment,
|
5980
6100
|
newNode,
|
5981
|
-
newChild
|
6101
|
+
newChild,
|
6102
|
+
nodeDisplay;
|
5982
6103
|
|
5983
6104
|
// Passes directly elemets with uneditable class
|
5984
6105
|
if (uneditableClass && oldNodeType === 1 && wysihtml5.dom.hasClass(oldNode, uneditableClass)) {
|
@@ -6005,7 +6126,13 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
|
|
6005
6126
|
}
|
6006
6127
|
}
|
6007
6128
|
|
6008
|
-
|
6129
|
+
nodeDisplay = wysihtml5.dom.getStyle("display").from(oldNode);
|
6130
|
+
|
6131
|
+
if (nodeDisplay === '') {
|
6132
|
+
// Handle display style when element not in dom
|
6133
|
+
nodeDisplay = wysihtml5.lang.array(blockElements).contains(oldNode.tagName) ? "block" : "";
|
6134
|
+
}
|
6135
|
+
if (wysihtml5.lang.array(["block", "flex", "table"]).contains(nodeDisplay)) {
|
6009
6136
|
fragment.appendChild(oldNode.ownerDocument.createElement("br"));
|
6010
6137
|
}
|
6011
6138
|
|
@@ -6481,15 +6608,14 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
|
|
6481
6608
|
}
|
6482
6609
|
}
|
6483
6610
|
|
6484
|
-
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
|
6485
6611
|
function _handleText(oldNode) {
|
6486
6612
|
var nextSibling = oldNode.nextSibling;
|
6487
6613
|
if (nextSibling && nextSibling.nodeType === wysihtml5.TEXT_NODE) {
|
6488
6614
|
// Concatenate text nodes
|
6489
|
-
nextSibling.data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "") + nextSibling.data.replace(INVISIBLE_SPACE_REG_EXP, "");
|
6615
|
+
nextSibling.data = oldNode.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "") + nextSibling.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
6490
6616
|
} else {
|
6491
6617
|
// \uFEFF = wysihtml5.INVISIBLE_SPACE (used as a hack in certain rich text editing situations)
|
6492
|
-
var data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "");
|
6618
|
+
var data = oldNode.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
6493
6619
|
return oldNode.ownerDocument.createTextNode(data);
|
6494
6620
|
}
|
6495
6621
|
}
|
@@ -8832,6 +8958,71 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8832
8958
|
return this.setSelection(range);
|
8833
8959
|
},
|
8834
8960
|
|
8961
|
+
// Constructs a self removing whitespace (ain absolute positioned span) for placing selection caret when normal methods fail.
|
8962
|
+
// Webkit has an issue with placing caret into places where there are no textnodes near by.
|
8963
|
+
creteTemporaryCaretSpaceAfter: function (node) {
|
8964
|
+
var caretPlaceholder = this.doc.createElement('span'),
|
8965
|
+
caretPlaceholderText = this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE),
|
8966
|
+
placeholderRemover = (function(event) {
|
8967
|
+
// Self-destructs the caret and keeps the text inserted into it by user
|
8968
|
+
var lastChild;
|
8969
|
+
|
8970
|
+
this.contain.removeEventListener('mouseup', placeholderRemover);
|
8971
|
+
this.contain.removeEventListener('keydown', keyDownHandler);
|
8972
|
+
this.contain.removeEventListener('touchstart', placeholderRemover);
|
8973
|
+
this.contain.removeEventListener('focus', placeholderRemover);
|
8974
|
+
this.contain.removeEventListener('blur', placeholderRemover);
|
8975
|
+
this.contain.removeEventListener('paste', delayedPlaceholderRemover);
|
8976
|
+
this.contain.removeEventListener('drop', delayedPlaceholderRemover);
|
8977
|
+
this.contain.removeEventListener('beforepaste', delayedPlaceholderRemover);
|
8978
|
+
|
8979
|
+
// If user inserted sth it is in the placeholder and sgould be unwrapped and stripped of invisible whitespace hack
|
8980
|
+
// Otherwise the wrapper can just be removed
|
8981
|
+
if (caretPlaceholder && caretPlaceholder.parentNode) {
|
8982
|
+
caretPlaceholder.innerHTML = caretPlaceholder.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
8983
|
+
if ((/[^\s]+/).test(caretPlaceholder.innerHTML)) {
|
8984
|
+
lastChild = caretPlaceholder.lastChild;
|
8985
|
+
wysihtml5.dom.unwrap(caretPlaceholder);
|
8986
|
+
this.setAfter(lastChild);
|
8987
|
+
} else {
|
8988
|
+
caretPlaceholder.parentNode.removeChild(caretPlaceholder);
|
8989
|
+
}
|
8990
|
+
|
8991
|
+
}
|
8992
|
+
}).bind(this),
|
8993
|
+
delayedPlaceholderRemover = function (event) {
|
8994
|
+
if (caretPlaceholder && caretPlaceholder.parentNode) {
|
8995
|
+
setTimeout(placeholderRemover, 0);
|
8996
|
+
}
|
8997
|
+
},
|
8998
|
+
keyDownHandler = function(event) {
|
8999
|
+
if (event.which !== 8 && event.which !== 91 && event.which !== 17 && (event.which !== 86 || (!event.ctrlKey && !event.metaKey))) {
|
9000
|
+
placeholderRemover();
|
9001
|
+
}
|
9002
|
+
};
|
9003
|
+
|
9004
|
+
caretPlaceholder.style.position = 'absolute';
|
9005
|
+
caretPlaceholder.style.display = 'block';
|
9006
|
+
caretPlaceholder.style.minWidth = '1px';
|
9007
|
+
caretPlaceholder.style.zIndex = '99999';
|
9008
|
+
caretPlaceholder.appendChild(caretPlaceholderText);
|
9009
|
+
|
9010
|
+
node.parentNode.insertBefore(caretPlaceholder, node.nextSibling);
|
9011
|
+
this.setBefore(caretPlaceholderText);
|
9012
|
+
|
9013
|
+
// Remove the caret fix on any of the following events (some are delayed as content change happens after event)
|
9014
|
+
this.contain.addEventListener('mouseup', placeholderRemover);
|
9015
|
+
this.contain.addEventListener('keydown', keyDownHandler);
|
9016
|
+
this.contain.addEventListener('touchstart', placeholderRemover);
|
9017
|
+
this.contain.addEventListener('focus', placeholderRemover);
|
9018
|
+
this.contain.addEventListener('blur', placeholderRemover);
|
9019
|
+
this.contain.addEventListener('paste', delayedPlaceholderRemover);
|
9020
|
+
this.contain.addEventListener('drop', delayedPlaceholderRemover);
|
9021
|
+
this.contain.addEventListener('beforepaste', delayedPlaceholderRemover);
|
9022
|
+
|
9023
|
+
return caretPlaceholder;
|
9024
|
+
},
|
9025
|
+
|
8835
9026
|
/**
|
8836
9027
|
* Set the caret after the given node
|
8837
9028
|
*
|
@@ -8840,11 +9031,23 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8840
9031
|
* selection.setBefore(myElement);
|
8841
9032
|
*/
|
8842
9033
|
setAfter: function(node) {
|
8843
|
-
var range = rangy.createRange(this.doc)
|
9034
|
+
var range = rangy.createRange(this.doc),
|
9035
|
+
originalScrollTop = this.doc.documentElement.scrollTop || this.doc.body.scrollTop || this.doc.defaultView.pageYOffset,
|
9036
|
+
originalScrollLeft = this.doc.documentElement.scrollLeft || this.doc.body.scrollLeft || this.doc.defaultView.pageXOffset,
|
9037
|
+
sel;
|
8844
9038
|
|
8845
9039
|
range.setStartAfter(node);
|
8846
9040
|
range.setEndAfter(node);
|
8847
|
-
|
9041
|
+
this.composer.element.focus();
|
9042
|
+
this.doc.defaultView.scrollTo(originalScrollLeft, originalScrollTop);
|
9043
|
+
sel = this.setSelection(range);
|
9044
|
+
|
9045
|
+
// Webkit fails to add selection if there are no textnodes in that region
|
9046
|
+
// (like an uneditable container at the end of content).
|
9047
|
+
if (!sel) {
|
9048
|
+
this.creteTemporaryCaretSpaceAfter(node);
|
9049
|
+
}
|
9050
|
+
return sel;
|
8848
9051
|
},
|
8849
9052
|
|
8850
9053
|
/**
|
@@ -8954,10 +9157,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8954
9157
|
return false;
|
8955
9158
|
},
|
8956
9159
|
|
8957
|
-
//
|
9160
|
+
// Deletes selection contents making sure uneditables/unselectables are not partially deleted
|
9161
|
+
// Triggers wysihtml5:uneditable:delete custom event on all deleted uneditables if customevents suppoorted
|
8958
9162
|
deleteContents: function() {
|
8959
9163
|
var range = this.getRange(),
|
8960
|
-
startParent, endParent;
|
9164
|
+
startParent, endParent, uneditables, ev;
|
8961
9165
|
|
8962
9166
|
if (this.unselectableClass) {
|
8963
9167
|
if ((startParent = wysihtml5.dom.getParentElement(range.startContainer, { className: this.unselectableClass }, false, this.contain))) {
|
@@ -8966,6 +9170,18 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8966
9170
|
if ((endParent = wysihtml5.dom.getParentElement(range.endContainer, { className: this.unselectableClass }, false, this.contain))) {
|
8967
9171
|
range.setEndAfter(endParent);
|
8968
9172
|
}
|
9173
|
+
|
9174
|
+
// If customevents present notify uneditable elements of being deleted
|
9175
|
+
uneditables = range.getNodes([1], (function (node) {
|
9176
|
+
return wysihtml5.dom.hasClass(node, this.unselectableClass);
|
9177
|
+
}).bind(this));
|
9178
|
+
for (var i = uneditables.length; i--;) {
|
9179
|
+
try {
|
9180
|
+
ev = new CustomEvent("wysihtml5:uneditable:delete");
|
9181
|
+
uneditables[i].dispatchEvent(ev);
|
9182
|
+
} catch (err) {}
|
9183
|
+
}
|
9184
|
+
|
8969
9185
|
}
|
8970
9186
|
range.deleteContents();
|
8971
9187
|
this.setSelection(range);
|
@@ -9385,6 +9601,24 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
9385
9601
|
selection.modify("extend", "right", "lineboundary");
|
9386
9602
|
},
|
9387
9603
|
|
9604
|
+
// collapses selection to current line beginning or end
|
9605
|
+
toLineBoundary: function (location, collapse) {
|
9606
|
+
collapse = (typeof collapse === 'undefined') ? false : collapse;
|
9607
|
+
if (wysihtml5.browser.supportsSelectionModify()) {
|
9608
|
+
var win = this.doc.defaultView,
|
9609
|
+
selection = win.getSelection();
|
9610
|
+
|
9611
|
+
selection.modify("extend", location, "lineboundary");
|
9612
|
+
if (collapse) {
|
9613
|
+
if (location === "left") {
|
9614
|
+
selection.collapseToStart();
|
9615
|
+
} else if (location === "right") {
|
9616
|
+
selection.collapseToEnd();
|
9617
|
+
}
|
9618
|
+
}
|
9619
|
+
}
|
9620
|
+
},
|
9621
|
+
|
9388
9622
|
_selectLine_MSIE: function() {
|
9389
9623
|
var range = this.doc.selection.createRange(),
|
9390
9624
|
rangeTop = range.boundingTop,
|
@@ -9550,10 +9784,14 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
9550
9784
|
return rangy.getSelection(this.doc.defaultView || this.doc.parentWindow);
|
9551
9785
|
},
|
9552
9786
|
|
9787
|
+
// Sets selection in document to a given range
|
9788
|
+
// Set selection method detects if it fails to set any selection in document and returns null on fail
|
9789
|
+
// (especially needed in webkit where some ranges just can not create selection for no reason)
|
9553
9790
|
setSelection: function(range) {
|
9554
9791
|
var win = this.doc.defaultView || this.doc.parentWindow,
|
9555
9792
|
selection = rangy.getSelection(win);
|
9556
|
-
|
9793
|
+
selection.setSingleRange(range);
|
9794
|
+
return (selection && selection.anchorNode && selection.focusNode) ? selection : null;
|
9557
9795
|
},
|
9558
9796
|
|
9559
9797
|
createRange: function() {
|
@@ -10304,6 +10542,13 @@ wysihtml5.Commands = Base.extend(
|
|
10304
10542
|
method = obj && obj.exec,
|
10305
10543
|
result = null;
|
10306
10544
|
|
10545
|
+
// If composer ahs placeholder unset it before command
|
10546
|
+
// Do not apply on commands that are behavioral
|
10547
|
+
if (this.composer.hasPlaceholderSet() && !wysihtml5.lang.array(['styleWithCSS', 'enableObjectResizing', 'enableInlineTableEditing']).contains(command)) {
|
10548
|
+
this.composer.element.innerHTML = "";
|
10549
|
+
this.composer.selection.selectNode(this.composer.element);
|
10550
|
+
}
|
10551
|
+
|
10307
10552
|
this.editor.fire("beforecommand:composer");
|
10308
10553
|
|
10309
10554
|
if (method) {
|
@@ -11412,7 +11657,7 @@ wysihtml5.commands.formatCode = {
|
|
11412
11657
|
|
11413
11658
|
// This space causes new lists to never break on enter
|
11414
11659
|
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
|
11415
|
-
tempElement.innerHTML = tempElement.innerHTML.replace(INVISIBLE_SPACE_REG_EXP, "");
|
11660
|
+
tempElement.innerHTML = tempElement.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
11416
11661
|
|
11417
11662
|
if (tempElement) {
|
11418
11663
|
isEmpty = wysihtml5.lang.array(["", "<br>", wysihtml5.INVISIBLE_SPACE]).contains(tempElement.innerHTML);
|
@@ -12777,6 +13022,22 @@ wysihtml5.views.View = Base.extend(
|
|
12777
13022
|
"85": "underline" // U
|
12778
13023
|
};
|
12779
13024
|
|
13025
|
+
// Adds multiple eventlisteners to target, bound to one callback
|
13026
|
+
// TODO: If needed elsewhere make it part of wysihtml5.dom or sth
|
13027
|
+
var addListeners = function (target, events, callback) {
|
13028
|
+
for(var i = 0, max = events.length; i < max; i++) {
|
13029
|
+
target.addEventListener(events[i], callback, false);
|
13030
|
+
}
|
13031
|
+
};
|
13032
|
+
|
13033
|
+
// Removes multiple eventlisteners from target, bound to one callback
|
13034
|
+
// TODO: If needed elsewhere make it part of wysihtml5.dom or sth
|
13035
|
+
var removeListeners = function (target, events, callback) {
|
13036
|
+
for(var i = 0, max = events.length; i < max; i++) {
|
13037
|
+
target.removeEventListener(events[i], callback, false);
|
13038
|
+
}
|
13039
|
+
};
|
13040
|
+
|
12780
13041
|
var deleteAroundEditable = function(selection, uneditable, element) {
|
12781
13042
|
// merge node with previous node from uneditable
|
12782
13043
|
var prevNode = selection.getPreviousNode(uneditable, true),
|
@@ -12811,7 +13072,10 @@ wysihtml5.views.View = Base.extend(
|
|
12811
13072
|
}
|
12812
13073
|
};
|
12813
13074
|
|
12814
|
-
var handleDeleteKeyPress = function(event,
|
13075
|
+
var handleDeleteKeyPress = function(event, composer) {
|
13076
|
+
var selection = composer.selection,
|
13077
|
+
element = composer.element;
|
13078
|
+
|
12815
13079
|
if (selection.isCollapsed()) {
|
12816
13080
|
if (selection.caretIsInTheBeginnig('LI')) {
|
12817
13081
|
event.preventDefault();
|
@@ -12870,248 +13134,258 @@ wysihtml5.views.View = Base.extend(
|
|
12870
13134
|
composer.commands.exec("insertHTML", " ");
|
12871
13135
|
};
|
12872
13136
|
|
12873
|
-
|
12874
|
-
|
12875
|
-
|
12876
|
-
|
12877
|
-
|
12878
|
-
|
12879
|
-
pasteEvents = ["drop", "paste", "beforepaste"],
|
12880
|
-
interactionEvents = ["drop", "paste", "mouseup", "focus", "keyup"];
|
12881
|
-
|
12882
|
-
// --------- destroy:composer event ---------
|
12883
|
-
dom.observe(container, "DOMNodeRemoved", function() {
|
12884
|
-
clearInterval(domNodeRemovedInterval);
|
12885
|
-
that.parent.fire("destroy:composer");
|
12886
|
-
});
|
12887
|
-
|
12888
|
-
// DOMNodeRemoved event is not supported in IE 8
|
12889
|
-
if (!browser.supportsMutationEvents()) {
|
12890
|
-
var domNodeRemovedInterval = setInterval(function() {
|
12891
|
-
if (!dom.contains(document.documentElement, container)) {
|
12892
|
-
clearInterval(domNodeRemovedInterval);
|
12893
|
-
that.parent.fire("destroy:composer");
|
12894
|
-
}
|
12895
|
-
}, 250);
|
12896
|
-
}
|
13137
|
+
var handleDomNodeRemoved = function(event) {
|
13138
|
+
if (this.domNodeRemovedInterval) {
|
13139
|
+
clearInterval(domNodeRemovedInterval);
|
13140
|
+
}
|
13141
|
+
this.parent.fire("destroy:composer");
|
13142
|
+
};
|
12897
13143
|
|
12898
|
-
|
13144
|
+
// Listens to "drop", "paste", "mouseup", "focus", "keyup" events and fires
|
13145
|
+
var handleUserInteraction = function (event) {
|
13146
|
+
this.parent.fire("beforeinteraction").fire("beforeinteraction:composer");
|
13147
|
+
setTimeout((function() {
|
13148
|
+
this.parent.fire("interaction").fire("interaction:composer");
|
13149
|
+
}).bind(this), 0);
|
13150
|
+
};
|
12899
13151
|
|
12900
|
-
|
12901
|
-
|
12902
|
-
that.parent.fire("interaction").fire("interaction:composer");
|
12903
|
-
}, 0);
|
12904
|
-
});
|
13152
|
+
var handleFocus = function(event) {
|
13153
|
+
this.parent.fire("focus", event).fire("focus:composer", event);
|
12905
13154
|
|
13155
|
+
// Delay storing of state until all focus handler are fired
|
13156
|
+
// especially the one which resets the placeholder
|
13157
|
+
setTimeout((function() {
|
13158
|
+
this.focusState = this.getValue(false, false);
|
13159
|
+
}).bind(this), 0);
|
13160
|
+
};
|
12906
13161
|
|
12907
|
-
|
12908
|
-
|
12909
|
-
|
12910
|
-
|
12911
|
-
|
12912
|
-
|
12913
|
-
that.tableClickHandle.stop();
|
12914
|
-
});
|
12915
|
-
} else {
|
12916
|
-
setTimeout(function() {
|
12917
|
-
that.doc.execCommand("enableObjectResizing", false, "false");
|
12918
|
-
that.doc.execCommand("enableInlineTableEditing", false, "false");
|
12919
|
-
}, 0);
|
12920
|
-
}
|
13162
|
+
var handleBlur = function(event) {
|
13163
|
+
if (this.focusState !== this.getValue(false, false)) {
|
13164
|
+
//create change event if supported (all except IE8)
|
13165
|
+
var changeevent = event;
|
13166
|
+
if(typeof Object.create == 'function') {
|
13167
|
+
changeevent = Object.create(event, { type: { value: 'change' } });
|
12921
13168
|
}
|
12922
|
-
this.
|
13169
|
+
this.parent.fire("change", changeevent).fire("change:composer", changeevent);
|
12923
13170
|
}
|
13171
|
+
this.parent.fire("blur", event).fire("blur:composer", event);
|
13172
|
+
};
|
12924
13173
|
|
12925
|
-
|
12926
|
-
|
12927
|
-
|
12928
|
-
|
12929
|
-
|
12930
|
-
|
12931
|
-
|
12932
|
-
|
12933
|
-
|
12934
|
-
dom.observe(focusBlurElement, "blur", function(event) {
|
12935
|
-
if (state !== that.getValue(false, false)) {
|
12936
|
-
//create change event if supported (all except IE8)
|
12937
|
-
var changeevent = event;
|
12938
|
-
if(typeof Object.create == 'function') {
|
12939
|
-
changeevent = Object.create(event, { type: { value: 'change' } });
|
12940
|
-
}
|
12941
|
-
that.parent.fire("change", changeevent).fire("change:composer", changeevent);
|
12942
|
-
}
|
12943
|
-
that.parent.fire("blur", event).fire("blur:composer", event);
|
12944
|
-
});
|
12945
|
-
|
12946
|
-
// --------- Drag & Drop logic ---------
|
12947
|
-
dom.observe(element, "dragenter", function() {
|
12948
|
-
that.parent.fire("unset_placeholder");
|
12949
|
-
});
|
12950
|
-
|
12951
|
-
dom.observe(element, pasteEvents, function(event) {
|
12952
|
-
that.parent.fire(event.type, event).fire(event.type + ":composer", event);
|
12953
|
-
});
|
12954
|
-
|
13174
|
+
var handlePaste = function(event) {
|
13175
|
+
this.parent.fire(event.type, event).fire(event.type + ":composer", event);
|
13176
|
+
if (event.type === "paste") {
|
13177
|
+
setTimeout((function() {
|
13178
|
+
this.parent.fire("newword:composer");
|
13179
|
+
}).bind(this), 0);
|
13180
|
+
}
|
13181
|
+
};
|
12955
13182
|
|
13183
|
+
var handleCopy = function(event) {
|
12956
13184
|
if (this.config.copyedFromMarking) {
|
12957
|
-
// If supported the copied source
|
13185
|
+
// If supported the copied source can be based directly on selection
|
12958
13186
|
// 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.
|
12959
|
-
|
12960
|
-
|
12961
|
-
|
12962
|
-
|
12963
|
-
event.preventDefault();
|
12964
|
-
}
|
12965
|
-
that.parent.fire(event.type, event).fire(event.type + ":composer", event);
|
12966
|
-
});
|
12967
|
-
}
|
12968
|
-
|
12969
|
-
// --------- neword event ---------
|
12970
|
-
dom.observe(element, "keyup", function(event) {
|
12971
|
-
var keyCode = event.keyCode;
|
12972
|
-
if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
|
12973
|
-
that.parent.fire("newword:composer");
|
13187
|
+
if (event.clipboardData) {
|
13188
|
+
event.clipboardData.setData("text/html", this.config.copyedFromMarking + this.selection.getHtml());
|
13189
|
+
event.clipboardData.setData("text/plain", this.selection.getPlainText());
|
13190
|
+
event.preventDefault();
|
12974
13191
|
}
|
12975
|
-
|
13192
|
+
this.parent.fire(event.type, event).fire(event.type + ":composer", event);
|
13193
|
+
}
|
13194
|
+
};
|
12976
13195
|
|
12977
|
-
|
12978
|
-
|
12979
|
-
|
13196
|
+
var handleKeyUp = function(event) {
|
13197
|
+
var keyCode = event.keyCode;
|
13198
|
+
if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
|
13199
|
+
this.parent.fire("newword:composer");
|
13200
|
+
}
|
13201
|
+
};
|
12980
13202
|
|
12981
|
-
|
13203
|
+
var handleMouseDown = function(event) {
|
12982
13204
|
if (!browser.canSelectImagesInContentEditable()) {
|
12983
|
-
|
12984
|
-
|
12985
|
-
|
12986
|
-
|
12987
|
-
|
13205
|
+
// Make sure that images are selected when clicking on them
|
13206
|
+
var target = event.target,
|
13207
|
+
allImages = this.element.querySelectorAll('img'),
|
13208
|
+
notMyImages = this.element.querySelectorAll('.' + this.config.uneditableContainerClassname + ' img'),
|
13209
|
+
myImages = wysihtml5.lang.array(allImages).without(notMyImages);
|
12988
13210
|
|
12989
|
-
|
12990
|
-
|
12991
|
-
|
12992
|
-
|
13211
|
+
if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
|
13212
|
+
this.selection.selectNode(target);
|
13213
|
+
}
|
13214
|
+
}
|
13215
|
+
};
|
13216
|
+
|
13217
|
+
// TODO: mouseover is not actually a foolproof and obvious place for this, must be changed as it modifies dom on random basis
|
13218
|
+
// Shows url in tooltip when hovering links or images
|
13219
|
+
var handleMouseOver = function(event) {
|
13220
|
+
var titlePrefixes = {
|
13221
|
+
IMG: "Image: ",
|
13222
|
+
A: "Link: "
|
13223
|
+
},
|
13224
|
+
target = event.target,
|
13225
|
+
nodeName = target.nodeName,
|
13226
|
+
title;
|
13227
|
+
|
13228
|
+
if (nodeName !== "A" && nodeName !== "IMG") {
|
13229
|
+
return;
|
12993
13230
|
}
|
13231
|
+
if(!target.hasAttribute("title")){
|
13232
|
+
title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
|
13233
|
+
target.setAttribute("title", title);
|
13234
|
+
}
|
13235
|
+
};
|
12994
13236
|
|
12995
|
-
|
12996
|
-
// If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
|
13237
|
+
var handleClick = function(event) {
|
12997
13238
|
if (this.config.uneditableContainerClassname) {
|
12998
|
-
|
12999
|
-
|
13000
|
-
|
13001
|
-
|
13002
|
-
|
13003
|
-
}
|
13239
|
+
// If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
|
13240
|
+
// If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
|
13241
|
+
var uneditable = wysihtml5.dom.getParentElement(event.target, { className: this.config.uneditableContainerClassname }, false, this.element);
|
13242
|
+
if (uneditable) {
|
13243
|
+
this.selection.setAfter(uneditable);
|
13244
|
+
}
|
13004
13245
|
}
|
13246
|
+
};
|
13005
13247
|
|
13248
|
+
var handleDrop = function(event) {
|
13006
13249
|
if (!browser.canSelectImagesInContentEditable()) {
|
13007
|
-
|
13008
|
-
|
13009
|
-
|
13010
|
-
|
13011
|
-
}, 0);
|
13012
|
-
});
|
13250
|
+
// TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
|
13251
|
+
setTimeout((function() {
|
13252
|
+
this.selection.getSelection().removeAllRanges();
|
13253
|
+
}).bind(this), 0);
|
13013
13254
|
}
|
13255
|
+
};
|
13014
13256
|
|
13015
|
-
|
13016
|
-
|
13017
|
-
|
13018
|
-
|
13019
|
-
}
|
13257
|
+
var handleKeyDown = function(event) {
|
13258
|
+
var keyCode = event.keyCode,
|
13259
|
+
command = shortcuts[keyCode],
|
13260
|
+
target, parent;
|
13020
13261
|
|
13021
|
-
|
13022
|
-
|
13023
|
-
|
13262
|
+
// Shortcut logic
|
13263
|
+
if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
|
13264
|
+
this.commands.exec(command);
|
13265
|
+
event.preventDefault();
|
13266
|
+
}
|
13024
13267
|
|
13025
|
-
|
13026
|
-
|
13027
|
-
|
13028
|
-
if (!event.shiftKey) {
|
13029
|
-
selection.collapseToStart();
|
13030
|
-
}
|
13031
|
-
}
|
13032
|
-
if (keyCode === 39) {
|
13033
|
-
selection.modify("extend", "right", "lineboundary");
|
13034
|
-
if (!event.shiftKey) {
|
13035
|
-
selection.collapseToEnd();
|
13036
|
-
}
|
13037
|
-
}
|
13038
|
-
event.preventDefault();
|
13039
|
-
}
|
13040
|
-
});
|
13268
|
+
if (keyCode === wysihtml5.BACKSPACE_KEY) {
|
13269
|
+
// Delete key override for special cases
|
13270
|
+
handleDeleteKeyPress(event, this);
|
13041
13271
|
}
|
13042
13272
|
|
13043
|
-
//
|
13044
|
-
|
13045
|
-
|
13046
|
-
|
13047
|
-
if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
|
13048
|
-
that.commands.exec(command);
|
13049
|
-
event.preventDefault();
|
13050
|
-
}
|
13051
|
-
if (keyCode === 8) {
|
13052
|
-
// delete key
|
13053
|
-
handleDeleteKeyPress(event, that.selection, element, that);
|
13054
|
-
} else if (that.config.handleTabKey && keyCode === 9) {
|
13273
|
+
// Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor
|
13274
|
+
if (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY) {
|
13275
|
+
target = this.selection.getSelectedNode(true);
|
13276
|
+
if (target && target.nodeName === "IMG") {
|
13055
13277
|
event.preventDefault();
|
13056
|
-
handleTabKeyDown(that, element);
|
13057
|
-
}
|
13058
|
-
});
|
13059
|
-
|
13060
|
-
// --------- Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor ---------
|
13061
|
-
dom.observe(element, "keydown", function(event) {
|
13062
|
-
var target = that.selection.getSelectedNode(true),
|
13063
|
-
keyCode = event.keyCode,
|
13064
|
-
parent;
|
13065
|
-
if (target && target.nodeName === "IMG" && (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY)) { // 8 => backspace, 46 => delete
|
13066
13278
|
parent = target.parentNode;
|
13067
|
-
|
13068
|
-
parent
|
13069
|
-
// and it's parent <a> too if it hasn't got any other child nodes
|
13279
|
+
parent.removeChild(target);// delete the <img>
|
13280
|
+
// And it's parent <a> too if it hasn't got any other child nodes
|
13070
13281
|
if (parent.nodeName === "A" && !parent.firstChild) {
|
13071
13282
|
parent.parentNode.removeChild(parent);
|
13072
13283
|
}
|
13073
|
-
|
13074
|
-
setTimeout(function() { wysihtml5.quirks.redraw(element); }, 0);
|
13075
|
-
event.preventDefault();
|
13076
|
-
}
|
13077
|
-
});
|
13078
|
-
|
13079
|
-
// --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the <body>) ---------
|
13080
|
-
if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
|
13081
|
-
dom.observe(container, "focus", function() {
|
13082
13284
|
setTimeout(function() {
|
13083
|
-
|
13084
|
-
that.focus();
|
13085
|
-
}
|
13285
|
+
wysihtml5.quirks.redraw(element);
|
13086
13286
|
}, 0);
|
13087
|
-
}
|
13287
|
+
}
|
13288
|
+
}
|
13088
13289
|
|
13089
|
-
|
13090
|
-
|
13091
|
-
|
13092
|
-
|
13093
|
-
});
|
13290
|
+
if (this.config.handleTabKey && keyCode === wysihtml5.TAB_KEY) {
|
13291
|
+
// TAB key handling
|
13292
|
+
event.preventDefault();
|
13293
|
+
handleTabKeyDown(this, element);
|
13094
13294
|
}
|
13095
13295
|
|
13096
|
-
|
13097
|
-
var titlePrefixes = {
|
13098
|
-
IMG: "Image: ",
|
13099
|
-
A: "Link: "
|
13100
|
-
};
|
13296
|
+
};
|
13101
13297
|
|
13102
|
-
|
13103
|
-
|
13104
|
-
|
13105
|
-
|
13106
|
-
if (nodeName !== "A" && nodeName !== "IMG") {
|
13107
|
-
return;
|
13298
|
+
var handleIframeFocus = function(event) {
|
13299
|
+
setTimeout((function() {
|
13300
|
+
if (this.doc.querySelector(":focus") !== this.element) {
|
13301
|
+
this.focus();
|
13108
13302
|
}
|
13109
|
-
|
13110
|
-
|
13111
|
-
|
13112
|
-
|
13303
|
+
}).bind(this), 0);
|
13304
|
+
};
|
13305
|
+
|
13306
|
+
var handleIframeBlur = function(event) {
|
13307
|
+
setTimeout((function() {
|
13308
|
+
this.selection.getSelection().removeAllRanges();
|
13309
|
+
}).bind(this), 0);
|
13310
|
+
};
|
13311
|
+
|
13312
|
+
// Table management
|
13313
|
+
// If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
|
13314
|
+
var initTableHandling = function () {
|
13315
|
+
var hideHandlers = function () {
|
13316
|
+
this.doc.execCommand("enableObjectResizing", false, "false");
|
13317
|
+
this.doc.execCommand("enableInlineTableEditing", false, "false");
|
13318
|
+
},
|
13319
|
+
iframeInitiator = (function() {
|
13320
|
+
hideHandlers.call(this);
|
13321
|
+
removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
13322
|
+
}).bind(this);
|
13323
|
+
|
13324
|
+
if( this.doc.execCommand &&
|
13325
|
+
wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
|
13326
|
+
wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
|
13327
|
+
{
|
13328
|
+
if (this.sandbox.getIframe) {
|
13329
|
+
addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
13330
|
+
} else {
|
13331
|
+
setTimeout((function() {
|
13332
|
+
hideHandlers.call(this);
|
13333
|
+
}).bind(this), 0);
|
13113
13334
|
}
|
13114
|
-
}
|
13335
|
+
}
|
13336
|
+
this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
|
13337
|
+
};
|
13338
|
+
|
13339
|
+
wysihtml5.views.Composer.prototype.observe = function() {
|
13340
|
+
var that = this,
|
13341
|
+
container = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
|
13342
|
+
element = this.element,
|
13343
|
+
focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? this.element : this.sandbox.getWindow();
|
13344
|
+
|
13345
|
+
this.focusState = this.getValue(false, false);
|
13346
|
+
|
13347
|
+
// --------- destroy:composer event ---------
|
13348
|
+
container.addEventListener(["DOMNodeRemoved"], handleDomNodeRemoved.bind(this), false);
|
13349
|
+
|
13350
|
+
// DOMNodeRemoved event is not supported in IE 8
|
13351
|
+
// TODO: try to figure out a polyfill style fix, so it could be transferred to polyfills and removed if ie8 is not needed
|
13352
|
+
if (!browser.supportsMutationEvents()) {
|
13353
|
+
this.domNodeRemovedInterval = setInterval(function() {
|
13354
|
+
if (!dom.contains(document.documentElement, container)) {
|
13355
|
+
handleDomNodeRemoved.call(this);
|
13356
|
+
}
|
13357
|
+
}, 250);
|
13358
|
+
}
|
13359
|
+
|
13360
|
+
// --------- User interactions --
|
13361
|
+
if (this.config.handleTables) {
|
13362
|
+
// If handleTables option is true, table handling functions are bound
|
13363
|
+
initTableHandling.call(this);
|
13364
|
+
}
|
13365
|
+
|
13366
|
+
addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
|
13367
|
+
focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
|
13368
|
+
focusBlurElement.addEventListener("blur", handleBlur.bind(this), false);
|
13369
|
+
|
13370
|
+
addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
|
13371
|
+
this.element.addEventListener("copy", handleCopy.bind(this), false);
|
13372
|
+
this.element.addEventListener("mousedown", handleMouseDown.bind(this), false);
|
13373
|
+
this.element.addEventListener("mouseover", handleMouseOver.bind(this), false);
|
13374
|
+
this.element.addEventListener("click", handleClick.bind(this), false);
|
13375
|
+
this.element.addEventListener("drop", handleDrop.bind(this), false);
|
13376
|
+
this.element.addEventListener("keyup", handleKeyUp.bind(this), false);
|
13377
|
+
this.element.addEventListener("keydown", handleKeyDown.bind(this), false);
|
13378
|
+
|
13379
|
+
this.element.addEventListener("dragenter", (function() {
|
13380
|
+
this.parent.fire("unset_placeholder");
|
13381
|
+
}).bind(this), false);
|
13382
|
+
|
13383
|
+
// --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the <body>) ---------
|
13384
|
+
if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
|
13385
|
+
container.addEventListener("focus", handleIframeFocus.bind(this), false);
|
13386
|
+
container.addEventListener("blur", handleIframeBlur.bind(this), false);
|
13387
|
+
}
|
13388
|
+
|
13115
13389
|
};
|
13116
13390
|
})(wysihtml5);
|
13117
13391
|
;/**
|