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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09f662c19d4439847f1ce68e8e8b2426d910e89e
4
- data.tar.gz: d20ca36a6b4d55f761c8d349d8ed0d850bdfd627
3
+ metadata.gz: f8b1f43eeac26ae1738b1861e4eda9180911bf2e
4
+ data.tar.gz: 591e2337f0b6730db35a2ee2134530ffac88d085
5
5
  SHA512:
6
- metadata.gz: 545f630a0749c55b84e7235d8183b63a8852099f869f6db594889fe48e2fb4f0fbea9b8b3df7811fcc0f9e6bd6c4ff1f8f6a114059acde363ae86bb74b77300f
7
- data.tar.gz: 78ff06fa47c2b931ae39e4facff9bf949d8a5a1d2faa31462955b77b3def8a0422e10e85316db9a47271deefd0e4a2dc4e30919abff7bb427d87da616710c672
6
+ metadata.gz: 4de851b6e42d4bc378fd054c730b001d4f6cab03007c198df769a70d6e9402fc553f00ca1a44d10ff879982cbd8e0874bf51d378f91978c6b8b040a862a4ddc2
7
+ data.tar.gz: 03ee2235f5dd67043c433dc3111f23b39b47ec7587bd0eb5222f77789138504f0a0f886a919bcfc69931aa399b1db3ea3db44b1d92ee3cb08a9904a299dc08d2
@@ -1,5 +1,5 @@
1
1
  module Wysihtml5x
2
2
  module Rails
3
- VERSION = "0.4.16"
3
+ VERSION = "0.4.17"
4
4
  end
5
5
  end
@@ -1,8 +1,84 @@
1
1
  // TODO: in future try to replace most inline compability checks with polyfills for code readability
2
2
 
3
- // element.textContent polyfill.
4
- // Unsupporting browsers: IE8
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.16
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.16",
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
- * http://code.google.com/p/rangy/
170
+ * https://github.com/timdown/rangy
66
171
  *
67
172
  * Copyright 2014, Tim Down
68
173
  * Licensed under the MIT license.
69
- * Version: 1.3alpha.20140804
70
- * Build date: 4 August 2014
174
+ * Version: 1.3.0-alpha.20140921
175
+ * Build date: 21 September 2014
71
176
  */
72
177
 
73
- (function(factory, global) {
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
- TODO: look into this properly.
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 a global variable
86
- global.rangy = factory();
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.3alpha.20140804",
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 (isHostObject(window, "console") && isHostMethod(window.console, "log")) {
182
- window.console.log(msg);
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
- window.alert(msg);
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 on this page in your browser. Reason: " + reason, api.config.alertOnFail);
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
- api.util.extend = function(obj, props, deep) {
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
- api.util.extend(o, p, true);
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
- try {
239
- if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
240
- toArray = function(arrayLike) {
241
- return slice.call(arrayLike, 0);
242
- };
243
- }
244
- } catch (e) {}
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
- api.util.toArray = toArray;
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 (isHostMethod(document, "addEventListener")) {
264
- addListener = function(obj, eventType, listener) {
265
- obj.addEventListener(eventType, listener, false);
266
- };
267
- } else if (isHostMethod(document, "attachEvent")) {
268
- addListener = function(obj, eventType, listener) {
269
- obj.attachEvent("on" + eventType, listener);
270
- };
271
- } else {
272
- fail("Document does not have required addEventListener or attachEvent method");
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
- api.util.addListener = addListener;
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
- api.shim = api.createMissingNativeApi = shim;
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(isCore, name, dependencies, initFunc) {
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(false, name, dependencies, initFunc);
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(true, name, dependencies, initFunc);
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 9 deleteContents() and extractContents() bug and correct it. See issue 107.
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
- this.nativeSelection.addRange(getNativeRange(range).cloneRange());
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
- * http://code.google.com/p/rangy/
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.3alpha.20140804
3808
- * Build date: 4 August 2014
3933
+ * Version: 1.3.0-alpha.20140921
3934
+ * Build date: 21 September 2014
3809
3935
  */
3810
- (function(factory, global) {
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
- } else if (typeof exports == "object") {
3816
- // Node/CommonJS style for Browserify
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(global.rangy);
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
- if (wysihtml5.dom.getStyle("display").from(oldNode) === "block") {
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
- return this.setSelection(range);
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
- // deletes selection contents making sure uneditables/unselectables are not partially deleted
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
- return selection.setSingleRange(range);
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, selection, element, composer) {
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", "&emsp;");
12871
13135
  };
12872
13136
 
12873
- wysihtml5.views.Composer.prototype.observe = function() {
12874
- var that = this,
12875
- state = this.getValue(false, false),
12876
- container = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
12877
- element = this.element,
12878
- focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? element : this.sandbox.getWindow(),
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
- // --------- User interaction tracking --
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
- dom.observe(focusBlurElement, interactionEvents, function() {
12901
- setTimeout(function() {
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
- if (this.config.handleTables) {
12908
- if(!this.tableClickHandle && this.doc.execCommand && wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") && wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing")) {
12909
- if (this.sandbox.getIframe) {
12910
- this.tableClickHandle = dom.observe(container , ["focus", "mouseup", "mouseover"], function() {
12911
- that.doc.execCommand("enableObjectResizing", false, "false");
12912
- that.doc.execCommand("enableInlineTableEditing", false, "false");
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.tableSelection = wysihtml5.quirks.tableCellsSelection(element, that.parent);
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
- // --------- Focus & blur logic ---------
12926
- dom.observe(focusBlurElement, "focus", function(event) {
12927
- that.parent.fire("focus", event).fire("focus:composer", event);
12928
-
12929
- // Delay storing of state until all focus handler are fired
12930
- // especially the one which resets the placeholder
12931
- setTimeout(function() { state = that.getValue(false, false); }, 0);
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 is based directly on selection
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
- dom.observe(element, "copy", function(event) {
12960
- if (event.clipboardData) {
12961
- event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
12962
- event.clipboardData.setData("text/plain", that.selection.getPlainText());
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
- this.parent.on("paste:composer", function() {
12978
- setTimeout(function() { that.parent.fire("newword:composer"); }, 0);
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
- // --------- Make sure that images are selected when clicking on them ---------
13203
+ var handleMouseDown = function(event) {
12982
13204
  if (!browser.canSelectImagesInContentEditable()) {
12983
- dom.observe(element, "mousedown", function(event) {
12984
- var target = event.target;
12985
- var allImages = element.querySelectorAll('img'),
12986
- notMyImages = element.querySelectorAll('.' + that.config.uneditableContainerClassname + ' img'),
12987
- myImages = wysihtml5.lang.array(allImages).without(notMyImages);
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
- if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
12990
- that.selection.selectNode(target);
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
- // If uneditables configured makes click on uneditable moves caret after clicked element (so it can be deleted like text)
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
- dom.observe(element, "click", function(event) {
12999
- var uneditable = wysihtml5.dom.getParentElement(event.target, { className: that.config.uneditableContainerClassname }, false, that.element);
13000
- if (uneditable) {
13001
- that.selection.setAfter(uneditable);
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
- dom.observe(element, "drop", function(event) {
13008
- // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
13009
- setTimeout(function() {
13010
- that.selection.getSelection().removeAllRanges();
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
- if (browser.hasHistoryIssue() && browser.supportsSelectionModify()) {
13016
- dom.observe(element, "keydown", function(event) {
13017
- if (!event.metaKey && !event.ctrlKey) {
13018
- return;
13019
- }
13257
+ var handleKeyDown = function(event) {
13258
+ var keyCode = event.keyCode,
13259
+ command = shortcuts[keyCode],
13260
+ target, parent;
13020
13261
 
13021
- var keyCode = event.keyCode,
13022
- win = element.ownerDocument.defaultView,
13023
- selection = win.getSelection();
13262
+ // Shortcut logic
13263
+ if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
13264
+ this.commands.exec(command);
13265
+ event.preventDefault();
13266
+ }
13024
13267
 
13025
- if (keyCode === 37 || keyCode === 39) {
13026
- if (keyCode === 37) {
13027
- selection.modify("extend", "left", "lineboundary");
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
- // --------- Shortcut logic ---------
13044
- dom.observe(element, "keydown", function(event) {
13045
- var keyCode = event.keyCode,
13046
- command = shortcuts[keyCode];
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
- // delete the <img>
13068
- parent.removeChild(target);
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
- if (that.doc.querySelector(":focus") !== that.element) {
13084
- that.focus();
13085
- }
13285
+ wysihtml5.quirks.redraw(element);
13086
13286
  }, 0);
13087
- });
13287
+ }
13288
+ }
13088
13289
 
13089
- dom.observe(this.element, "blur", function() {
13090
- setTimeout(function() {
13091
- that.selection.getSelection().removeAllRanges();
13092
- }, 0);
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
- // --------- Show url in tooltip when hovering links or images ---------
13097
- var titlePrefixes = {
13098
- IMG: "Image: ",
13099
- A: "Link: "
13100
- };
13296
+ };
13101
13297
 
13102
- dom.observe(element, "mouseover", function(event) {
13103
- var target = event.target,
13104
- nodeName = target.nodeName,
13105
- title;
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
- var hasTitle = target.hasAttribute("title");
13110
- if(!hasTitle){
13111
- title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
13112
- target.setAttribute("title", title);
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
  ;/**