wysihtml5x-rails 0.4.16 → 0.4.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8b1f43eeac26ae1738b1861e4eda9180911bf2e
|
4
|
+
data.tar.gz: 591e2337f0b6730db35a2ee2134530ffac88d085
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4de851b6e42d4bc378fd054c730b001d4f6cab03007c198df769a70d6e9402fc553f00ca1a44d10ff879982cbd8e0874bf51d378f91978c6b8b040a862a4ddc2
|
7
|
+
data.tar.gz: 03ee2235f5dd67043c433dc3111f23b39b47ec7587bd0eb5222f77789138504f0a0f886a919bcfc69931aa399b1db3ea3db44b1d92ee3cb08a9904a299dc08d2
|
@@ -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
|
;/**
|