bootstrap-wysihtml5-rails 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -18,4 +18,5 @@ end
18
18
  desc "Publish the gem"
19
19
  task 'publish' do
20
20
  system("gem push bootstrap-wysihtml5-rails-#{BootstrapWysihtml5Rails::Rails::VERSION}.gem")
21
+ system("git push")
21
22
  end
@@ -1,5 +1,5 @@
1
1
  module BootstrapWysihtml5Rails
2
2
  module Rails
3
- VERSION = "0.2.4"
3
+ VERSION = "0.2.5"
4
4
  end
5
5
  end
@@ -19,14 +19,54 @@
19
19
  //,+ "<a class='btn' data-wysihtml5-command='underline' title='CTRL+U'>Underline</a>"
20
20
  + "</div>"
21
21
  + "</li>",
22
- "lists": "<li>"
23
- + "<div class='btn-group'>"
24
- + "<a class='btn' data-wysihtml5-command='insertUnorderedList' title='Unordered List'><i class='icon-list'></i></a>"
25
- + "<a class='btn' data-wysihtml5-command='insertOrderedList' title='Ordered List'><i class='icon-th-list'></i></a>"
26
- + "<a class='btn' data-wysihtml5-command='Outdent' title='Outdent'><i class='icon-indent-right'></i></a>"
27
- + "<a class='btn' data-wysihtml5-command='Indent' title='Indent'><i class='icon-indent-left'></i></a>"
28
- + "</div>"
29
- + "</li>",
22
+ "lists": "<li>"
23
+ + "<div class='btn-group'>"
24
+ + "<a class='btn' data-wysihtml5-command='insertUnorderedList' title='Unordered List'><i class='icon-list'></i></a>"
25
+ + "<a class='btn' data-wysihtml5-command='insertOrderedList' title='Ordered List'><i class='icon-th-list'></i></a>"
26
+ + "<a class='btn' data-wysihtml5-command='Outdent' title='Outdent'><i class='icon-indent-right'></i></a>"
27
+ + "<a class='btn' data-wysihtml5-command='Indent' title='Indent'><i class='icon-indent-left'></i></a>"
28
+ + "</div>"
29
+ + "</li>",
30
+
31
+ "link": "<li>"
32
+
33
+ + "<div class='bootstrap-wysihtml5-insert-link-modal modal hide fade'>"
34
+ + "<div class='modal-header'>"
35
+ + "<a class='close' data-dismiss='modal'>×</a>"
36
+ + "<h3>Insert Link</h3>"
37
+ + "</div>"
38
+ + "<div class='modal-body'>"
39
+ + "<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>"
40
+ + "</div>"
41
+ + "<div class='modal-footer'>"
42
+ + "<a href='#' class='btn' data-dismiss='modal'>Cancel</a>"
43
+ + "<a href='#' class='btn btn-primary' data-dismiss='modal'>Insert link</a>"
44
+ + "</div>"
45
+ + "</div>"
46
+
47
+ + "<a class='btn' data-wysihtml5-command='createLink' title='Link'><i class='icon-share'></i></a>"
48
+
49
+ + "</li>",
50
+
51
+ "image": "<li>"
52
+
53
+ + "<div class='bootstrap-wysihtml5-insert-image-modal modal hide fade'>"
54
+ + "<div class='modal-header'>"
55
+ + "<a class='close' data-dismiss='modal'>×</a>"
56
+ + "<h3>Insert Image</h3>"
57
+ + "</div>"
58
+ + "<div class='modal-body'>"
59
+ + "<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>"
60
+ + "</div>"
61
+ + "<div class='modal-footer'>"
62
+ + "<a href='#' class='btn' data-dismiss='modal'>Cancel</a>"
63
+ + "<a href='#' class='btn btn-primary' data-dismiss='modal'>Insert image</a>"
64
+ + "</div>"
65
+ + "</div>"
66
+
67
+ + "<a class='btn' data-wysihtml5-command='insertImage' title='Insert image'><i class='icon-picture'></i></a>"
68
+
69
+ + "</li>",
30
70
 
31
71
  "html":
32
72
  "<li>"
@@ -41,6 +81,8 @@
41
81
  "emphasis": true,
42
82
  "lists": true,
43
83
  "html": false,
84
+ "link": true,
85
+ "image": false,
44
86
  events: {},
45
87
  parserRules: {
46
88
  tags: {
@@ -53,6 +95,14 @@
53
95
  "h1": {},
54
96
  "h2": {},
55
97
  "u": 1,
98
+ "img": {
99
+ "check_attributes": {
100
+ "width": "numbers",
101
+ "alt": "alt",
102
+ "src": "url",
103
+ "height": "numbers"
104
+ }
105
+ },
56
106
  "a": {
57
107
  set_attributes: {
58
108
  target: "_blank",
@@ -70,6 +120,8 @@
70
120
  this.el = el;
71
121
  this.toolbar = this.createToolbar(el, options || defaultOptions);
72
122
  this.editor = this.createEditor(options);
123
+
124
+ window.editor = this.editor;
73
125
 
74
126
  $('iframe.wysihtml5-sandbox').each(function(i, el){
75
127
  $(el.contentWindow).off('focus.wysihtml5').on({
@@ -78,6 +130,8 @@
78
130
  }
79
131
  });
80
132
  });
133
+
134
+
81
135
  };
82
136
 
83
137
  Wysihtml5.prototype = {
@@ -105,11 +159,12 @@
105
159
  },
106
160
 
107
161
  createToolbar: function(el, options) {
162
+ var self = this;
108
163
  var toolbar = $("<ul/>", {
109
- id : el.attr('id') + "-wysihtml5-toolbar",
110
- class : "wysihtml5-toolbar",
111
- style: "display:none"
112
- });
164
+ 'id' : el.attr('id') + "-wysihtml5-toolbar",
165
+ 'class' : "wysihtml5-toolbar",
166
+ 'style': "display:none"
167
+ });
113
168
 
114
169
  for(var key in defaultOptions) {
115
170
  var value = false;
@@ -126,10 +181,15 @@
126
181
  toolbar.append(templates[key]);
127
182
 
128
183
  if(key == "html") {
129
- var changeViewSelector = "a[data-wysihtml5-action='change_view']";
130
- toolbar.find(changeViewSelector).click(function(e) {
131
- toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
132
- });
184
+ this.initHtml(toolbar);
185
+ }
186
+
187
+ if(key == "link") {
188
+ this.initInsertLink(toolbar);
189
+ }
190
+
191
+ if(key == "image") {
192
+ this.initInsertImage(toolbar);
133
193
  }
134
194
  }
135
195
  }
@@ -144,6 +204,91 @@
144
204
  this.el.before(toolbar);
145
205
 
146
206
  return toolbar;
207
+ },
208
+
209
+ initHtml: function(toolbar) {
210
+ var changeViewSelector = "a[data-wysihtml5-action='change_view']";
211
+ toolbar.find(changeViewSelector).click(function(e) {
212
+ toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
213
+ });
214
+ },
215
+
216
+ initInsertImage: function(toolbar) {
217
+ var self = this;
218
+ var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
219
+ var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
220
+ var insertButton = insertImageModal.find('a.btn-primary');
221
+
222
+ var insertImage = function() {
223
+ var url = urlInput.val();
224
+ urlInput.val('');
225
+ self.editor.composer.commands.exec("createLink", {
226
+ href: url,
227
+ target: "_blank",
228
+ rel: "nofollow"
229
+ });
230
+ };
231
+
232
+ urlInput.keypress(function(e) {
233
+ if(e.which == 13) {
234
+ insertImage();
235
+ insertImageModal.modal('hide');
236
+ }
237
+ });
238
+
239
+ insertButton.click(insertImage);
240
+
241
+ insertImageModal.on('shown', function() {
242
+ urlInput.focus();
243
+ });
244
+
245
+ insertImageModal.on('hide', function() {
246
+ self.editor.currentView.element.focus();
247
+ });
248
+
249
+ toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
250
+ insertImageModal.modal('show');
251
+ });
252
+ },
253
+
254
+ initInsertLink: function(toolbar) {
255
+ var self = this;
256
+ var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
257
+ var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
258
+ var insertButton = insertLinkModal.find('a.btn-primary');
259
+ var initialValue = urlInput.val();
260
+
261
+ var insertLink = function() {
262
+ var url = urlInput.val();
263
+ urlInput.val(initialValue);
264
+ self.editor.composer.commands.exec("createLink", {
265
+ href: url,
266
+ target: "_blank",
267
+ rel: "nofollow"
268
+ });
269
+ };
270
+ var pressedEnter = false;
271
+
272
+ urlInput.keypress(function(e) {
273
+ if(e.which == 13) {
274
+ insertLink();
275
+ insertLinkModal.modal('hide');
276
+ }
277
+ });
278
+
279
+ insertButton.click(insertLink);
280
+
281
+ insertLinkModal.on('shown', function() {
282
+ urlInput.focus();
283
+ });
284
+
285
+ insertLinkModal.on('hide', function() {
286
+ self.editor.currentView.element.focus();
287
+ });
288
+
289
+ toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
290
+ insertLinkModal.modal('show');
291
+ });
147
292
  }
148
293
  };
149
294
 
@@ -156,4 +301,4 @@
156
301
 
157
302
  $.fn.wysihtml5.Constructor = Wysihtml5;
158
303
 
159
- }(window.jQuery, window.wysihtml5);
304
+ }(window.jQuery, window.wysihtml5);
@@ -1,15 +1,15 @@
1
1
  /**
2
- * @license wysihtml5 v0.3.0_rc1
2
+ * @license wysihtml5 v0.3.0_rc2
3
3
  * https://github.com/xing/wysihtml5
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
6
6
  *
7
- * Copyright (C) 2011 XING AG
8
- * Licensed under GNU General Public License
7
+ * Copyright (C) 2012 XING AG
8
+ * Licensed under the MIT license (MIT)
9
9
  *
10
10
  */
11
11
  var wysihtml5 = {
12
- version: "0.3.0_rc1",
12
+ version: "0.3.0_rc2",
13
13
 
14
14
  // namespaces
15
15
  commands: {},
@@ -3383,8 +3383,6 @@ Base = Base.extend({
3383
3383
  }
3384
3384
  });/**
3385
3385
  * Detect browser support for specific features
3386
- *
3387
- * @author Christopher Blum <christopher.blum@xing.com>
3388
3386
  */
3389
3387
  wysihtml5.browser = (function() {
3390
3388
  var userAgent = navigator.userAgent,
@@ -3393,6 +3391,7 @@ wysihtml5.browser = (function() {
3393
3391
  isIE = userAgent.indexOf("MSIE") !== -1 && userAgent.indexOf("Opera") === -1,
3394
3392
  isGecko = userAgent.indexOf("Gecko") !== -1 && userAgent.indexOf("KHTML") === -1,
3395
3393
  isWebKit = userAgent.indexOf("AppleWebKit/") !== -1,
3394
+ isChrome = userAgent.indexOf("Chrome/") !== -1,
3396
3395
  isOpera = userAgent.indexOf("Opera/") !== -1;
3397
3396
 
3398
3397
  function iosVersion(userAgent) {
@@ -3716,6 +3715,24 @@ wysihtml5.browser = (function() {
3716
3715
  */
3717
3716
  crashesWhenDefineProperty: function(property) {
3718
3717
  return isIE && (property === "XMLHttpRequest" || property === "XDomainRequest");
3718
+ },
3719
+
3720
+ /**
3721
+ * IE is the only browser who fires the "focus" event not immediately when .focus() is called on an element
3722
+ */
3723
+ doesAsyncFocus: function() {
3724
+ return isIE;
3725
+ },
3726
+
3727
+ /**
3728
+ * In IE it's impssible for the user and for the selection library to set the caret after an <img> when it's the lastChild in the document
3729
+ */
3730
+ hasProblemsSettingCaretAfterImg: function() {
3731
+ return isIE;
3732
+ },
3733
+
3734
+ hasUndoInContextMenu: function() {
3735
+ return isGecko || isChrome || isOpera;
3719
3736
  }
3720
3737
  };
3721
3738
  })();wysihtml5.lang.array = function(arr) {
@@ -3905,7 +3922,7 @@ wysihtml5.browser = (function() {
3905
3922
  * Inspired by http://james.padolsey.com/javascript/find-and-replace-text-with-javascript/
3906
3923
  *
3907
3924
  * @param {Element} element Container element in which to search for urls
3908
- * @author Christopher Blum <christopher.blum@xing.com>
3925
+ *
3909
3926
  * @example
3910
3927
  * <div id="text-container">Please click here: www.google.com</div>
3911
3928
  * <script>wysihtml5.dom.autoLink(document.getElementById("text-container"));</script>
@@ -4089,7 +4106,6 @@ wysihtml5.dom.contains = (function() {
4089
4106
  })();/**
4090
4107
  * Converts an HTML fragment/element into a unordered/ordered list
4091
4108
  *
4092
- * @author Christopher Blum <christopher.blum@xing.com>
4093
4109
  * @param {Element} element The element which should be turned into a list
4094
4110
  * @param {String} listType The list type in which to convert the tree (either "ul" or "ol")
4095
4111
  * @return {Element} The created list
@@ -4170,8 +4186,6 @@ wysihtml5.dom.convertToList = (function() {
4170
4186
  })();/**
4171
4187
  * Copy a set of attributes from one element to another
4172
4188
  *
4173
- * @author Christopher Blum <christopher.blum@xing.com>
4174
- *
4175
4189
  * @param {Array} attributesToCopy List of attributes which should be copied
4176
4190
  * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
4177
4191
  * copy the attributes from., this again returns an object which provides a method named "to" which can be invoked
@@ -4210,8 +4224,6 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
4210
4224
  *
4211
4225
  * Interesting article on how to copy styles
4212
4226
  *
4213
- * @author Christopher Blum <christopher.blum@xing.com>
4214
- *
4215
4227
  * @param {Array} stylesToCopy List of styles which should be copied
4216
4228
  * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
4217
4229
  * copy the styles from., this again returns an object which provides a method named "to" which can be invoked
@@ -4306,7 +4318,6 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
4306
4318
  * Fixing IE's inability to treat unknown elements (HTML5 section, article, ...) correctly
4307
4319
  * when inserted via innerHTML
4308
4320
  *
4309
- * @author Christopher Blum <christopher.blum@xing.com>
4310
4321
  * @param {String} html The html which should be wrapped in a dom element
4311
4322
  * @param {Obejct} [context] Document object of the context the html belongs to
4312
4323
  *
@@ -4368,7 +4379,6 @@ wysihtml5.dom.getAsDom = (function() {
4368
4379
  * Walks the dom tree from the given node up until it finds a match
4369
4380
  * Designed for optimal performance.
4370
4381
  *
4371
- * @author Christopher Blum <christopher.blum@xing.com>
4372
4382
  * @param {Element} node The from which to check the parent nodes
4373
4383
  * @param {Object} matchingSet Object to match against (possible properties: nodeName, className, classRegExp)
4374
4384
  * @param {Number} [levels] How many parents should the function check up from the current node (defaults to 50)
@@ -4446,7 +4456,7 @@ wysihtml5.dom.getParentElement = (function() {
4446
4456
  *
4447
4457
  * @param {Element} element The element on which to retrieve the style
4448
4458
  * @param {String} property The CSS property to retrieve ("float", "display", "text-align", ...)
4449
- * @author Christopher Blum <christopher.blum@xing.com>
4459
+ *
4450
4460
  * @example
4451
4461
  * wysihtml5.dom.getStyle("display").from(document.body);
4452
4462
  * // => "block"
@@ -4485,11 +4495,11 @@ wysihtml5.dom.getStyle = (function() {
4485
4495
  // gives you the original "50%".
4486
4496
  // Opera supports both, currentStyle and window.getComputedStyle, that's why checking for currentStyle should have higher prio
4487
4497
  if (currentStyle) {
4488
- try {
4498
+ try {
4489
4499
  return currentStyle[camelizedProperty];
4490
- } catch(e) {
4491
- //ie fails for some reason. need to investigate
4492
- }
4500
+ } catch(e) {
4501
+ //ie will occasionally fail for unknown reasons. swallowing exception
4502
+ }
4493
4503
  }
4494
4504
 
4495
4505
  var win = doc.defaultView || doc.parentWindow,
@@ -4518,7 +4528,6 @@ wysihtml5.dom.getStyle = (function() {
4518
4528
  * Optimized for being heavily executed
4519
4529
  * Unleashes the power of live node lists
4520
4530
  *
4521
- * @author Christopher Blum <christopher.blum@xing.com>
4522
4531
  * @param {Object} doc The document object of the context where to check
4523
4532
  * @param {String} tagName Upper cased tag name
4524
4533
  * @example
@@ -4546,7 +4555,6 @@ wysihtml5.dom.hasElementWithTagName = (function() {
4546
4555
  * Optimized for being heavily executed
4547
4556
  * Unleashes the power of live node lists
4548
4557
  *
4549
- * @author Christopher Blum <christopher.blum@xing.com>
4550
4558
  * @param {Object} doc The document object of the context where to check
4551
4559
  * @param {String} tagName Upper cased tag name
4552
4560
  * @example
@@ -4614,8 +4622,6 @@ wysihtml5.dom.insert = function(elementToInsert) {
4614
4622
  };/**
4615
4623
  * Method to set dom events
4616
4624
  *
4617
- * @author Christopher Blum <christopher.blum@xing.com>
4618
- *
4619
4625
  * @example
4620
4626
  * wysihtml5.dom.observe(iframe.contentWindow.document.body, ["focus", "blur"], function() { ... });
4621
4627
  */
@@ -4668,8 +4674,6 @@ wysihtml5.dom.observe = function(element, eventNames, handler) {
4668
4674
  * HTML Sanitizer
4669
4675
  * Rewrites the HTML based on given rules
4670
4676
  *
4671
- * @author Christopher Blum <christopher.blum@xing.com>
4672
- *
4673
4677
  * @param {Element|String} elementOrHtml HTML String to be sanitized OR element whose content should be sanitized
4674
4678
  * @param {Object} [rules] List of rules for rewriting the HTML, if there's no rule for an element it will
4675
4679
  * be converted to a "span". Each rule is a key/value pair where key is the tag to convert, and value the
@@ -5115,7 +5119,6 @@ wysihtml5.dom.parse = (function() {
5115
5119
  })();/**
5116
5120
  * Checks for empty text node childs and removes them
5117
5121
  *
5118
- * @author Christopher Blum <christopher.blum@xing.com>
5119
5122
  * @param {Element} node The element in which to cleanup
5120
5123
  * @example
5121
5124
  * wysihtml5.dom.removeEmptyTextNodes(element);
@@ -5135,7 +5138,6 @@ wysihtml5.dom.removeEmptyTextNodes = function(node) {
5135
5138
  /**
5136
5139
  * Renames an element (eg. a <div> to a <p>) and keeps its childs
5137
5140
  *
5138
- * @author Christopher Blum <christopher.blum@xing.com>
5139
5141
  * @param {Element} element The list element which should be renamed
5140
5142
  * @param {Element} newNodeName The desired tag name
5141
5143
  *
@@ -5170,7 +5172,6 @@ wysihtml5.dom.renameElement = function(element, newNodeName) {
5170
5172
  };/**
5171
5173
  * Takes an element, removes it and replaces it with it's childs
5172
5174
  *
5173
- * @author Christopher Blum <christopher.blum@xing.com>
5174
5175
  * @param {Object} node The node which to replace with it's child nodes
5175
5176
  * @example
5176
5177
  * <div id="foo">
@@ -5201,7 +5202,6 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5201
5202
  /**
5202
5203
  * Unwraps an unordered/ordered list
5203
5204
  *
5204
- * @author Christopher Blum <christopher.blum@xing.com>
5205
5205
  * @param {Element} element The list element which should be unwrapped
5206
5206
  *
5207
5207
  * @example
@@ -5285,10 +5285,8 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5285
5285
  * - therefore the "allow-scripts" flag is needed, which then would deactivate any security, as the js executed inside the iframe
5286
5286
  * can do anything as if the sandbox attribute wasn't set
5287
5287
  *
5288
- * @author Christopher Blum <christopher.blum@xing.com>
5289
- *
5290
5288
  * @param {Function} [readyCallback] Method that gets invoked when the sandbox is ready
5291
- * @param {Object} [config] Optional parameters, see defaultConfig property for more info
5289
+ * @param {Object} [config] Optional parameters
5292
5290
  *
5293
5291
  * @example
5294
5292
  * new wysihtml5.dom.Sandbox(function(sandbox) {
@@ -5299,9 +5297,6 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5299
5297
  var /**
5300
5298
  * Default configuration
5301
5299
  */
5302
- defaultConfig = {
5303
- uaCompatible: "IE=Edge" // X-UA-Compatible meta tag value (Document compatibility mode)
5304
- },
5305
5300
  doc = document,
5306
5301
  /**
5307
5302
  * Properties to unset/protect on the window object
@@ -5332,7 +5327,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5332
5327
 
5333
5328
  constructor: function(readyCallback, config) {
5334
5329
  this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
5335
- this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
5330
+ this.config = wysihtml5.lang.object({}).merge(config).get();
5336
5331
  this.iframe = this._createIframe();
5337
5332
  },
5338
5333
 
@@ -5429,12 +5424,11 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5429
5424
 
5430
5425
  var that = this,
5431
5426
  iframeWindow = iframe.contentWindow,
5432
- iframeDocument = iframe.contentDocument || iframe.contentWindow.document,
5427
+ iframeDocument = iframe.contentWindow.document,
5433
5428
  charset = doc.characterSet || doc.charset || "utf-8",
5434
5429
  sandboxHtml = this._getHtml({
5435
5430
  charset: charset,
5436
- stylesheets: this.config.stylesheets,
5437
- uaCompatible: this.config.uaCompatible
5431
+ stylesheets: this.config.stylesheets
5438
5432
  });
5439
5433
 
5440
5434
  // Create the basic dom tree including proper DOCTYPE and charset
@@ -5442,8 +5436,8 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5442
5436
  iframeDocument.write(sandboxHtml);
5443
5437
  iframeDocument.close();
5444
5438
 
5445
- this.getWindow = function() { return iframeWindow; };
5446
- this.getDocument = function() { return iframeDocument; };
5439
+ this.getWindow = function() { return iframe.contentWindow; };
5440
+ this.getDocument = function() { return iframe.contentWindow.document; };
5447
5441
 
5448
5442
  // Catch js errors and pass them to the parent's onerror event
5449
5443
  // addEventListener("error") doesn't work properly in some browsers
@@ -5496,7 +5490,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5496
5490
 
5497
5491
  return wysihtml5.lang.string(
5498
5492
  '<!DOCTYPE html><html><head>'
5499
- + '<meta http-equiv="X-UA-Compatible" content="#{uaCompatible}"><meta charset="#{charset}">#{stylesheets}</head>'
5493
+ + '<meta charset="#{charset}">#{stylesheets}</head>'
5500
5494
  + '<body></body></html>'
5501
5495
  ).interpolate(templateVars);
5502
5496
  },
@@ -5567,8 +5561,6 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
5567
5561
  * - div[contentEditable] elements don't support it
5568
5562
  * - older browsers (such as IE8 and Firefox 3.6) don't support it at all
5569
5563
  *
5570
- * @author Christopher Blum <christopher.blum@xing.com>
5571
- *
5572
5564
  * @param {Object} parent Instance of main wysihtml5.Editor class
5573
5565
  * @param {Element} view Instance of wysihtml5.views.* class
5574
5566
  * @param {String} placeholderText
@@ -5679,7 +5671,6 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5679
5671
  })();/**
5680
5672
  * IE and Opera leave an empty paragraph in the contentEditable element after clearing it
5681
5673
  *
5682
- * @author Christopher Blum <christopher.blum@xing.com>
5683
5674
  * @param {Object} contentEditableElement The contentEditable element to observe for clearing events
5684
5675
  * @exaple
5685
5676
  * wysihtml5.quirks.ensureProperClearing(myContentEditableElement);
@@ -5699,8 +5690,8 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5699
5690
  }, 0);
5700
5691
  };
5701
5692
 
5702
- return function(contentEditableElement) {
5703
- dom.observe(contentEditableElement, ["cut", "keydown"], clearIfNecessary);
5693
+ return function(composer) {
5694
+ dom.observe(composer.element, ["cut", "keydown"], clearIfNecessary);
5704
5695
  };
5705
5696
  })();
5706
5697
 
@@ -5709,7 +5700,6 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5709
5700
  /**
5710
5701
  * In Opera when the caret is in the first and only item of a list (<ul><li>|</li></ul>) and the list is the first child of the contentEditable element, it's impossible to delete the list by hitting backspace
5711
5702
  *
5712
- * @author Christopher Blum <christopher.blum@xing.com>
5713
5703
  * @param {Object} contentEditableElement The contentEditable element to observe for clearing events
5714
5704
  * @exaple
5715
5705
  * wysihtml5.quirks.ensureProperClearing(myContentEditableElement);
@@ -5745,14 +5735,14 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5745
5735
  list.parentNode.removeChild(list);
5746
5736
  };
5747
5737
 
5748
- return function(contentEditableElement) {
5749
- dom.observe(contentEditableElement, "keydown", function(event) {
5738
+ return function(composer) {
5739
+ dom.observe(composer.element, "keydown", function(event) {
5750
5740
  if (event.keyCode !== wysihtml5.BACKSPACE_KEY) {
5751
5741
  return;
5752
5742
  }
5753
5743
 
5754
- var element = wysihtml5.selection.getSelectedNode();
5755
- clearIfNecessary(element, contentEditableElement);
5744
+ var element = composer.selection.getSelectedNode();
5745
+ clearIfNecessary(element, composer.element);
5756
5746
  });
5757
5747
  };
5758
5748
  })();
@@ -5792,8 +5782,6 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5792
5782
  * - Opera & IE insert new <p> on return
5793
5783
  * - Chrome & Safari insert new <div> on return
5794
5784
  * - Firefox inserts <br> on return (yippie!)
5795
-
5796
- * @author Christopher Blum <christopher.blum@xing.com>
5797
5785
  *
5798
5786
  * @param {Element} element
5799
5787
  *
@@ -5805,72 +5793,71 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5805
5793
  USE_NATIVE_LINE_BREAK_WHEN_CARET_INSIDE_TAGS = ["LI", "P", "H1", "H2", "H3", "H4", "H5", "H6"],
5806
5794
  LIST_TAGS = ["UL", "OL", "MENU"];
5807
5795
 
5808
- function unwrap(selectedNode) {
5809
- var parentElement = dom.getParentElement(selectedNode, { nodeName: ["P", "DIV"] }, 2);
5810
- if (!parentElement) {
5811
- return;
5812
- }
5813
-
5814
- var invisibleSpace = document.createTextNode(wysihtml5.INVISIBLE_SPACE);
5815
- dom.insert(invisibleSpace).before(parentElement);
5816
- dom.replaceWithChildNodes(parentElement);
5817
- wysihtml5.selection.selectNode(invisibleSpace);
5818
- }
5819
-
5820
- function keyDown(event) {
5821
- var keyCode = event.keyCode;
5822
- if (event.shiftKey || (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY)) {
5823
- return;
5796
+ wysihtml5.quirks.insertLineBreakOnReturn = function(composer) {
5797
+ function unwrap(selectedNode) {
5798
+ var parentElement = dom.getParentElement(selectedNode, { nodeName: ["P", "DIV"] }, 2);
5799
+ if (!parentElement) {
5800
+ return;
5801
+ }
5802
+
5803
+ var invisibleSpace = document.createTextNode(wysihtml5.INVISIBLE_SPACE);
5804
+ dom.insert(invisibleSpace).before(parentElement);
5805
+ dom.replaceWithChildNodes(parentElement);
5806
+ composer.selection.selectNode(invisibleSpace);
5824
5807
  }
5825
-
5826
- var element = event.target,
5827
- selectedNode = wysihtml5.selection.getSelectedNode(),
5828
- blockElement = dom.getParentElement(selectedNode, { nodeName: USE_NATIVE_LINE_BREAK_WHEN_CARET_INSIDE_TAGS }, 4);
5829
- if (blockElement) {
5830
- // Some browsers create <p> elements after leaving a list
5831
- // check after keydown of backspace and return whether a <p> got inserted and unwrap it
5832
- if (blockElement.nodeName === "LI" && (keyCode === wysihtml5.ENTER_KEY || keyCode === wysihtml5.BACKSPACE_KEY)) {
5833
- setTimeout(function() {
5834
- var selectedNode = wysihtml5.selection.getSelectedNode(),
5835
- list,
5836
- div;
5837
- if (!selectedNode) {
5838
- return;
5839
- }
5840
-
5841
- list = dom.getParentElement(selectedNode, {
5842
- nodeName: LIST_TAGS
5843
- }, 2);
5844
-
5845
- if (list) {
5846
- return;
5847
- }
5848
-
5849
- unwrap(selectedNode);
5850
- }, 0);
5851
- } else if (blockElement.nodeName.match(/H[1-6]/) && keyCode === wysihtml5.ENTER_KEY) {
5852
- setTimeout(function() {
5853
- unwrap(wysihtml5.selection.getSelectedNode());
5854
- }, 0);
5855
- }
5856
- return;
5808
+
5809
+ function keyDown(event) {
5810
+ var keyCode = event.keyCode;
5811
+ if (event.shiftKey || (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY)) {
5812
+ return;
5813
+ }
5814
+
5815
+ var element = event.target,
5816
+ selectedNode = composer.selection.getSelectedNode(),
5817
+ blockElement = dom.getParentElement(selectedNode, { nodeName: USE_NATIVE_LINE_BREAK_WHEN_CARET_INSIDE_TAGS }, 4);
5818
+ if (blockElement) {
5819
+ // Some browsers create <p> elements after leaving a list
5820
+ // check after keydown of backspace and return whether a <p> got inserted and unwrap it
5821
+ if (blockElement.nodeName === "LI" && (keyCode === wysihtml5.ENTER_KEY || keyCode === wysihtml5.BACKSPACE_KEY)) {
5822
+ setTimeout(function() {
5823
+ var selectedNode = composer.selection.getSelectedNode(),
5824
+ list,
5825
+ div;
5826
+ if (!selectedNode) {
5827
+ return;
5828
+ }
5829
+
5830
+ list = dom.getParentElement(selectedNode, {
5831
+ nodeName: LIST_TAGS
5832
+ }, 2);
5833
+
5834
+ if (list) {
5835
+ return;
5836
+ }
5837
+
5838
+ unwrap(selectedNode);
5839
+ }, 0);
5840
+ } else if (blockElement.nodeName.match(/H[1-6]/) && keyCode === wysihtml5.ENTER_KEY) {
5841
+ setTimeout(function() {
5842
+ unwrap(composer.selection.getSelectedNode());
5843
+ }, 0);
5844
+ }
5845
+ return;
5846
+ }
5847
+
5848
+ if (keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
5849
+ composer.commands.exec("insertLineBreak");
5850
+ event.preventDefault();
5851
+ }
5857
5852
  }
5858
5853
 
5859
- if (keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
5860
- wysihtml5.commands.exec("insertLineBreak");
5861
- event.preventDefault();
5862
- }
5863
- }
5864
-
5865
- wysihtml5.quirks.insertLineBreakOnReturn = function(element) {
5866
5854
  // keypress doesn't fire when you hit backspace
5867
- dom.observe(element.ownerDocument, "keydown", keyDown);
5855
+ dom.observe(composer.element.ownerDocument, "keydown", keyDown);
5868
5856
  };
5869
5857
  })(wysihtml5);/**
5870
5858
  * Force rerendering of a given element
5871
5859
  * Needed to fix display misbehaviors of IE
5872
5860
  *
5873
- * @author Christopher Blum <christopher.blum@xing.com>
5874
5861
  * @param {Element} element The element object which needs to be rerendered
5875
5862
  * @example
5876
5863
  * wysihtml5.quirks.redraw(document.body);
@@ -5889,7 +5876,13 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5889
5876
  doc.execCommand("italic", false, null);
5890
5877
  } catch(e) {}
5891
5878
  };
5892
- })(wysihtml5);(function(wysihtml5) {
5879
+ })(wysihtml5);/**
5880
+ * Selection API
5881
+ *
5882
+ * @example
5883
+ * var selection = new wysihtml5.Selection(editor);
5884
+ */
5885
+ (function(wysihtml5) {
5893
5886
  var dom = wysihtml5.dom;
5894
5887
 
5895
5888
  function _getCumulativeOffsetTop(element) {
@@ -5903,18 +5896,15 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5903
5896
  return top;
5904
5897
  }
5905
5898
 
5906
- wysihtml5.selection = {
5907
-
5908
- /**
5909
- * Setup selection for editor
5910
- *
5911
- * @param {Object} doc Document object of the context
5912
- */
5913
- initialize: function(doc) {
5899
+ wysihtml5.Selection = Base.extend(
5900
+ /** @scope wysihtml5.Selection.prototype */ {
5901
+ constructor: function(editor) {
5914
5902
  // Make sure that our external range library is initialized
5915
5903
  window.rangy.init();
5916
5904
 
5917
- this.doc = doc;
5905
+ this.editor = editor;
5906
+ this.composer = editor.composer;
5907
+ this.doc = this.composer.doc;
5918
5908
  },
5919
5909
 
5920
5910
  /**
@@ -5928,7 +5918,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5928
5918
  },
5929
5919
 
5930
5920
  /**
5931
- * Restore a selection retrieved via wysihtml5.selection.getBookmark
5921
+ * Restore a selection retrieved via wysihtml5.Selection.prototype.getBookmark
5932
5922
  *
5933
5923
  * @param {Object} bookmark An object that represents the current selection
5934
5924
  */
@@ -5945,7 +5935,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5945
5935
  *
5946
5936
  * @param {Object} node The element or text node where to position the caret in front of
5947
5937
  * @example
5948
- * wysihtml5.selection.setBefore(myElement);
5938
+ * selection.setBefore(myElement);
5949
5939
  */
5950
5940
  setBefore: function(node) {
5951
5941
  var range = rangy.createRange(this.doc);
@@ -5959,7 +5949,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5959
5949
  *
5960
5950
  * @param {Object} node The element or text node where to position the caret in front of
5961
5951
  * @example
5962
- * wysihtml5.selection.setBefore(myElement);
5952
+ * selection.setBefore(myElement);
5963
5953
  */
5964
5954
  setAfter: function(node) {
5965
5955
  var range = rangy.createRange(this.doc);
@@ -5973,7 +5963,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
5973
5963
  *
5974
5964
  * @param {Element} node The node/element to select
5975
5965
  * @example
5976
- * wysihtml5.selection.selectNode(document.getElementById("my-image"));
5966
+ * selection.selectNode(document.getElementById("my-image"));
5977
5967
  */
5978
5968
  selectNode: function(node) {
5979
5969
  var range = rangy.createRange(this.doc),
@@ -6011,7 +6001,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6011
6001
  * @param {Boolean} [controlRange] (only IE) Whether it should return the selected ControlRange element when the selection type is a "ControlRange"
6012
6002
  * @return {Object} The node that contains the caret
6013
6003
  * @example
6014
- * var nodeThatContainsCaret = wysihtml5.selection.getSelectedNode();
6004
+ * var nodeThatContainsCaret = selection.getSelectedNode();
6015
6005
  */
6016
6006
  getSelectedNode: function(controlRange) {
6017
6007
  var selection,
@@ -6128,7 +6118,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6128
6118
  *
6129
6119
  * @param {String} html HTML string to insert
6130
6120
  * @example
6131
- * wysihtml5.selection.insertHTML("<p>foobar</p>");
6121
+ * selection.insertHTML("<p>foobar</p>");
6132
6122
  */
6133
6123
  insertHTML: function(html) {
6134
6124
  var range = rangy.createRange(this.doc),
@@ -6145,7 +6135,7 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6145
6135
  *
6146
6136
  * @param {Object} node HTML string to insert
6147
6137
  * @example
6148
- * wysihtml5.selection.insertNode(document.createTextNode("foobar"));
6138
+ * selection.insertNode(document.createTextNode("foobar"));
6149
6139
  */
6150
6140
  insertNode: function(node) {
6151
6141
  var range = this.getRange();
@@ -6180,11 +6170,10 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6180
6170
  * Scroll the current caret position into the view
6181
6171
  * FIXME: This is a bit hacky, there might be a smarter way of doing this
6182
6172
  *
6183
- * @param {Object} element A scrollable element in which the caret is currently positioned
6184
6173
  * @example
6185
- * wysihtml5.selection.scrollIntoView(element);
6174
+ * selection.scrollIntoView();
6186
6175
  */
6187
- scrollIntoView: function(element) {
6176
+ scrollIntoView: function() {
6188
6177
  var doc = this.doc,
6189
6178
  hasScrollBars = doc.documentElement.scrollHeight > doc.documentElement.offsetHeight,
6190
6179
  tempElement = doc._wysihtml5ScrollIntoViewElement = doc._wysihtml5ScrollIntoViewElement || (function() {
@@ -6199,8 +6188,8 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6199
6188
  this.insertNode(tempElement);
6200
6189
  offsetTop = _getCumulativeOffsetTop(tempElement);
6201
6190
  tempElement.parentNode.removeChild(tempElement);
6202
- if (offsetTop > element.scrollTop) {
6203
- element.scrollTop = offsetTop;
6191
+ if (offsetTop > doc.body.scrollTop) {
6192
+ doc.body.scrollTop = offsetTop;
6204
6193
  }
6205
6194
  }
6206
6195
  },
@@ -6302,18 +6291,16 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6302
6291
  selection = rangy.getSelection(win);
6303
6292
  return selection.setSingleRange(range);
6304
6293
  }
6305
- };
6294
+ });
6306
6295
 
6307
6296
  })(wysihtml5);
6308
-
6309
6297
  /**
6310
- * @author Christopher Blum <christopher.blum@xing.com> to match WYSIHTML5 logic
6311
- * in order to be able ...
6298
+ * Inspired by the rangy CSS Applier module written by Tim Down and licensed under the MIT license.
6299
+ * http://code.google.com/p/rangy/
6300
+ *
6301
+ * changed in order to be able ...
6312
6302
  * - to use custom tags
6313
6303
  * - to detect and replace similar css classes via reg exp
6314
- *
6315
- * Inspired by the rangy CSS Applier module written by Tim Down and licensed under the MIT license.
6316
- * http://code.google.com/p/rangy/
6317
6304
  */
6318
6305
  (function(wysihtml5, rangy) {
6319
6306
  var defaultTagName = "span";
@@ -6742,22 +6729,23 @@ wysihtml5.quirks.cleanPastedHTML = (function() {
6742
6729
  })(wysihtml5, rangy);/**
6743
6730
  * Rich Text Query/Formatting Commands
6744
6731
  *
6745
- * @author Christopher Blum <christopher.blum@xing.com>
6732
+ * @example
6733
+ * var commands = new wysihtml5.Commands(editor);
6746
6734
  */
6747
- wysihtml5.commands = {
6748
- initialize: function(editor) {
6735
+ wysihtml5.Commands = Base.extend(
6736
+ /** @scope wysihtml5.Commands.prototype */ {
6737
+ constructor: function(editor) {
6749
6738
  this.editor = editor;
6750
- this.element = editor.composer.element;
6751
- this.doc = this.element.ownerDocument;
6739
+ this.composer = editor.composer;
6740
+ this.doc = this.composer.doc;
6752
6741
  },
6753
6742
 
6754
6743
  /**
6755
6744
  * Check whether the browser supports the given command
6756
6745
  *
6757
- * @param {Object} element The element which has contentEditable=true
6758
6746
  * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
6759
6747
  * @example
6760
- * wysihtml5.commands.supports("createLink");
6748
+ * commands.supports("createLink");
6761
6749
  */
6762
6750
  support: function(command) {
6763
6751
  return wysihtml5.browser.supportsCommand(this.doc, command);
@@ -6766,20 +6754,19 @@ wysihtml5.commands = {
6766
6754
  /**
6767
6755
  * Check whether the browser supports the given command
6768
6756
  *
6769
- * @param {Object} element The element which has contentEditable=true
6770
6757
  * @param {String} command The command string which to execute (eg. "bold", "italic", "insertUnorderedList")
6771
6758
  * @param {String} [value] The command value parameter, needed for some commands ("createLink", "insertImage", ...), optional for commands that don't require one ("bold", "underline", ...)
6772
6759
  * @example
6773
- * wysihtml5.commands.exec("insertImage", "http://a1.twimg.com/profile_images/113868655/schrei_twitter_reasonably_small.jpg");
6760
+ * commands.exec("insertImage", "http://a1.twimg.com/profile_images/113868655/schrei_twitter_reasonably_small.jpg");
6774
6761
  */
6775
6762
  exec: function(command, value) {
6776
- var obj = this[command],
6763
+ var obj = wysihtml5.commands[command],
6777
6764
  method = obj && obj.exec;
6778
6765
 
6779
6766
  this.editor.fire("beforecommand:composer");
6780
6767
 
6781
6768
  if (method) {
6782
- return method.call(obj, this.element, command, value);
6769
+ return method.call(obj, this.composer, command, value);
6783
6770
  } else {
6784
6771
  try {
6785
6772
  // try/catch for buggy firefox
@@ -6794,18 +6781,17 @@ wysihtml5.commands = {
6794
6781
  * Check whether the current command is active
6795
6782
  * If the caret is within a bold text, then calling this with command "bold" should return true
6796
6783
  *
6797
- * @param {Object} element The element which has contentEditable=true
6798
6784
  * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
6799
6785
  * @param {String} [commandValue] The command value parameter (eg. for "insertImage" the image src)
6800
6786
  * @return {Boolean} Whether the command is active
6801
6787
  * @example
6802
- * var isCurrentSelectionBold = wysihtml5.commands.state("bold");
6788
+ * var isCurrentSelectionBold = commands.state("bold");
6803
6789
  */
6804
6790
  state: function(command, commandValue) {
6805
- var obj = this[command],
6791
+ var obj = wysihtml5.commands[command],
6806
6792
  method = obj && obj.state;
6807
6793
  if (method) {
6808
- return method.call(obj, this.element, command, commandValue);
6794
+ return method.call(obj, this.composer, command, commandValue);
6809
6795
  } else {
6810
6796
  try {
6811
6797
  // try/catch for buggy firefox
@@ -6819,17 +6805,16 @@ wysihtml5.commands = {
6819
6805
  /**
6820
6806
  * Get the current command's value
6821
6807
  *
6822
- * @param {Object} element The element which has contentEditable=true
6823
6808
  * @param {String} command The command string which to check (eg. "formatBlock")
6824
6809
  * @return {String} The command value
6825
6810
  * @example
6826
- * var currentBlockElement = wysihtml5.commands.value("formatBlock");
6811
+ * var currentBlockElement = commands.value("formatBlock");
6827
6812
  */
6828
6813
  value: function(command) {
6829
- var obj = this[command],
6814
+ var obj = wysihtml5.commands[command],
6830
6815
  method = obj && obj.value;
6831
6816
  if (method) {
6832
- method(this.element, command);
6817
+ return method.call(obj, this.composer, command);
6833
6818
  } else {
6834
6819
  try {
6835
6820
  // try/catch for buggy firefox
@@ -6839,21 +6824,21 @@ wysihtml5.commands = {
6839
6824
  }
6840
6825
  }
6841
6826
  }
6842
- };(function(wysihtml5) {
6827
+ });(function(wysihtml5) {
6843
6828
  var undef;
6844
6829
 
6845
6830
  wysihtml5.commands.bold = {
6846
- exec: function(element, command) {
6847
- return wysihtml5.commands.formatInline.exec(element, command, "b");
6831
+ exec: function(composer, command) {
6832
+ return wysihtml5.commands.formatInline.exec(composer, command, "b");
6848
6833
  },
6849
6834
 
6850
- state: function(element, command, color) {
6835
+ state: function(composer, command, color) {
6851
6836
  // element.ownerDocument.queryCommandState("bold") results:
6852
6837
  // firefox: only <b>
6853
6838
  // chrome: <b>, <strong>, <h1>, <h2>, ...
6854
6839
  // ie: <b>, <strong>
6855
6840
  // opera: <b>, <strong>
6856
- return wysihtml5.commands.formatInline.state(element, command, "b");
6841
+ return wysihtml5.commands.formatInline.state(composer, command, "b");
6857
6842
  },
6858
6843
 
6859
6844
  value: function() {
@@ -6867,9 +6852,9 @@ wysihtml5.commands = {
6867
6852
  NODE_NAME = "A",
6868
6853
  dom = wysihtml5.dom;
6869
6854
 
6870
- function _removeFormat(element, anchors) {
6871
- var length = anchors.length,
6872
- i = 0,
6855
+ function _removeFormat(composer, anchors) {
6856
+ var length = anchors.length,
6857
+ i = 0,
6873
6858
  anchor,
6874
6859
  codeElement,
6875
6860
  textContent;
@@ -6889,8 +6874,8 @@ wysihtml5.commands = {
6889
6874
  }
6890
6875
  }
6891
6876
 
6892
- function _format(element, attributes) {
6893
- var doc = element.ownerDocument,
6877
+ function _format(composer, attributes) {
6878
+ var doc = composer.doc,
6894
6879
  tempClass = "_wysihtml5-temp-" + (+new Date()),
6895
6880
  tempClassRegExp = /non-matching-class/g,
6896
6881
  i = 0,
@@ -6903,7 +6888,7 @@ wysihtml5.commands = {
6903
6888
  textContent,
6904
6889
  whiteSpace,
6905
6890
  j;
6906
- wysihtml5.commands.formatInline.exec(element, undef, NODE_NAME, tempClass, tempClassRegExp);
6891
+ wysihtml5.commands.formatInline.exec(composer, undef, NODE_NAME, tempClass, tempClassRegExp);
6907
6892
  anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
6908
6893
  length = anchors.length;
6909
6894
  for (; i<length; i++) {
@@ -6922,12 +6907,12 @@ wysihtml5.commands = {
6922
6907
  if (!hasElementChild && isEmpty) {
6923
6908
  dom.setTextContent(anchor, anchor.href);
6924
6909
  whiteSpace = doc.createTextNode(" ");
6925
- wysihtml5.selection.setAfter(anchor);
6926
- wysihtml5.selection.insertNode(whiteSpace);
6910
+ composer.selection.setAfter(anchor);
6911
+ composer.selection.insertNode(whiteSpace);
6927
6912
  elementToSetCaretAfter = whiteSpace;
6928
6913
  }
6929
6914
  }
6930
- wysihtml5.selection.setAfter(elementToSetCaretAfter);
6915
+ composer.selection.setAfter(elementToSetCaretAfter);
6931
6916
  }
6932
6917
 
6933
6918
  wysihtml5.commands.createLink = {
@@ -6940,28 +6925,26 @@ wysihtml5.commands = {
6940
6925
  *
6941
6926
  * @example
6942
6927
  * // either ...
6943
- * wysihtml5.commands.createLink.exec(element, "createLink", "http://www.google.de");
6928
+ * wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
6944
6929
  * // ... or ...
6945
- * wysihtml5.commands.createLink.exec(element, "createLink", { href: "http://www.google.de", target: "_blank" });
6930
+ * wysihtml5.commands.createLink.exec(composer, "createLink", { href: "http://www.google.de", target: "_blank" });
6946
6931
  */
6947
- exec: function(element, command, value) {
6948
- var doc = element.ownerDocument,
6949
- anchors = this.state(element, command);
6950
-
6932
+ exec: function(composer, command, value) {
6933
+ var anchors = this.state(composer, command);
6951
6934
  if (anchors) {
6952
6935
  // Selection contains links
6953
- wysihtml5.selection.executeAndRestore(function() {
6954
- _removeFormat(element, anchors);
6936
+ composer.selection.executeAndRestore(function() {
6937
+ _removeFormat(composer, anchors);
6955
6938
  });
6956
6939
  } else {
6957
6940
  // Create links
6958
6941
  value = typeof(value) === "object" ? value : { href: value };
6959
- _format(element, value);
6942
+ _format(composer, value);
6960
6943
  }
6961
6944
  },
6962
6945
 
6963
- state: function(element, command) {
6964
- return wysihtml5.commands.formatInline.state(element, command, "A");
6946
+ state: function(composer, command) {
6947
+ return wysihtml5.commands.formatInline.state(composer, command, "A");
6965
6948
  },
6966
6949
 
6967
6950
  value: function() {
@@ -6978,12 +6961,12 @@ wysihtml5.commands = {
6978
6961
  REG_EXP = /wysiwyg-font-size-[a-z]+/g;
6979
6962
 
6980
6963
  wysihtml5.commands.fontSize = {
6981
- exec: function(element, command, size) {
6982
- return wysihtml5.commands.formatInline.exec(element, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
6964
+ exec: function(composer, command, size) {
6965
+ return wysihtml5.commands.formatInline.exec(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
6983
6966
  },
6984
6967
 
6985
- state: function(element, command, size) {
6986
- return wysihtml5.commands.formatInline.state(element, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
6968
+ state: function(composer, command, size) {
6969
+ return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
6987
6970
  },
6988
6971
 
6989
6972
  value: function() {
@@ -7001,12 +6984,12 @@ wysihtml5.commands = {
7001
6984
  REG_EXP = /wysiwyg-color-[a-z]+/g;
7002
6985
 
7003
6986
  wysihtml5.commands.foreColor = {
7004
- exec: function(element, command, color) {
7005
- return wysihtml5.commands.formatInline.exec(element, command, "span", "wysiwyg-color-" + color, REG_EXP);
6987
+ exec: function(composer, command, color) {
6988
+ return wysihtml5.commands.formatInline.exec(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
7006
6989
  },
7007
6990
 
7008
- state: function(element, command, color) {
7009
- return wysihtml5.commands.formatInline.state(element, command, "span", "wysiwyg-color-" + color, REG_EXP);
6991
+ state: function(composer, command, color) {
6992
+ return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
7010
6993
  },
7011
6994
 
7012
6995
  value: function() {
@@ -7016,7 +6999,6 @@ wysihtml5.commands = {
7016
6999
  })(wysihtml5);(function(wysihtml5) {
7017
7000
  var undef,
7018
7001
  dom = wysihtml5.dom,
7019
- selection = wysihtml5.selection,
7020
7002
  DEFAULT_NODE_NAME = "DIV",
7021
7003
  // Following elements are grouped
7022
7004
  // when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
@@ -7140,7 +7122,7 @@ wysihtml5.commands = {
7140
7122
  if (target.nodeType !== wysihtml5.ELEMENT_NODE) {
7141
7123
  return;
7142
7124
  }
7143
- displayStyle = dom.getStyle("display").from(target);
7125
+ displayStyle = dom.getStyle("display").from(target);
7144
7126
  if (displayStyle.substr(0, 6) !== "inline") {
7145
7127
  // Make sure that only block elements receive the given class
7146
7128
  target.className += " " + className;
@@ -7153,12 +7135,12 @@ wysihtml5.commands = {
7153
7135
  }
7154
7136
  }
7155
7137
 
7156
- function _selectLineAndWrap(element) {
7157
- selection.selectLine();
7158
- selection.surround(element);
7138
+ function _selectLineAndWrap(composer, element) {
7139
+ composer.selection.selectLine();
7140
+ composer.selection.surround(element);
7159
7141
  _removeLineBreakBeforeAndAfter(element);
7160
7142
  _removeLastChildIfLineBreak(element);
7161
- selection.selectNode(element);
7143
+ composer.selection.selectNode(element);
7162
7144
  }
7163
7145
 
7164
7146
  function _hasClasses(element) {
@@ -7166,15 +7148,15 @@ wysihtml5.commands = {
7166
7148
  }
7167
7149
 
7168
7150
  wysihtml5.commands.formatBlock = {
7169
- exec: function(element, command, nodeName, className, classRegExp) {
7170
- var doc = element.ownerDocument,
7171
- blockElement = this.state(element, command, nodeName, className, classRegExp),
7151
+ exec: function(composer, command, nodeName, className, classRegExp) {
7152
+ var doc = composer.doc,
7153
+ blockElement = this.state(composer, command, nodeName, className, classRegExp),
7172
7154
  selectedNode;
7173
7155
 
7174
7156
  nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
7175
7157
 
7176
7158
  if (blockElement) {
7177
- selection.executeAndRestoreSimple(function() {
7159
+ composer.selection.executeAndRestoreSimple(function() {
7178
7160
  if (classRegExp) {
7179
7161
  _removeClass(blockElement, classRegExp);
7180
7162
  }
@@ -7194,13 +7176,13 @@ wysihtml5.commands = {
7194
7176
 
7195
7177
  // Find similiar block element and rename it (<h2 class="foo"></h2> => <h1 class="foo"></h1>)
7196
7178
  if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
7197
- selectedNode = selection.getSelectedNode();
7179
+ selectedNode = composer.selection.getSelectedNode();
7198
7180
  blockElement = dom.getParentElement(selectedNode, {
7199
- nodeName: BLOCK_ELEMENTS_GROUP
7181
+ nodeName: BLOCK_ELEMENTS_GROUP
7200
7182
  });
7201
7183
 
7202
7184
  if (blockElement) {
7203
- selection.executeAndRestoreSimple(function() {
7185
+ composer.selection.executeAndRestoreSimple(function() {
7204
7186
  // Rename current block element to new block element and add class
7205
7187
  if (nodeName) {
7206
7188
  blockElement = dom.renameElement(blockElement, nodeName);
@@ -7213,7 +7195,7 @@ wysihtml5.commands = {
7213
7195
  }
7214
7196
  }
7215
7197
 
7216
- if (wysihtml5.commands.support(command)) {
7198
+ if (composer.commands.support(command)) {
7217
7199
  _execCommand(doc, command, nodeName || DEFAULT_NODE_NAME, className);
7218
7200
  return;
7219
7201
  }
@@ -7222,12 +7204,12 @@ wysihtml5.commands = {
7222
7204
  if (className) {
7223
7205
  blockElement.className = className;
7224
7206
  }
7225
- _selectLineAndWrap(blockElement);
7207
+ _selectLineAndWrap(composer, blockElement);
7226
7208
  },
7227
7209
 
7228
- state: function(element, command, nodeName, className, classRegExp) {
7210
+ state: function(composer, command, nodeName, className, classRegExp) {
7229
7211
  nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
7230
- var selectedNode = selection.getSelectedNode();
7212
+ var selectedNode = composer.selection.getSelectedNode();
7231
7213
  return dom.getParentElement(selectedNode, {
7232
7214
  nodeName: nodeName,
7233
7215
  className: className,
@@ -7297,17 +7279,17 @@ wysihtml5.commands = {
7297
7279
  }
7298
7280
 
7299
7281
  wysihtml5.commands.formatInline = {
7300
- exec: function(element, command, tagName, className, classRegExp) {
7301
- var range = wysihtml5.selection.getRange();
7282
+ exec: function(composer, command, tagName, className, classRegExp) {
7283
+ var range = composer.selection.getRange();
7302
7284
  if (!range) {
7303
7285
  return false;
7304
7286
  }
7305
7287
  _getApplier(tagName, className, classRegExp).toggleRange(range);
7306
- wysihtml5.selection.setSelection(range);
7288
+ composer.selection.setSelection(range);
7307
7289
  },
7308
7290
 
7309
- state: function(element, command, tagName, className, classRegExp) {
7310
- var doc = element.ownerDocument,
7291
+ state: function(composer, command, tagName, className, classRegExp) {
7292
+ var doc = composer.doc,
7311
7293
  aliasTagName = ALIAS_MAPPING[tagName] || tagName,
7312
7294
  range;
7313
7295
 
@@ -7322,7 +7304,7 @@ wysihtml5.commands = {
7322
7304
  return false;
7323
7305
  }
7324
7306
 
7325
- range = wysihtml5.selection.getRange();
7307
+ range = composer.selection.getRange();
7326
7308
  if (!range) {
7327
7309
  return false;
7328
7310
  }
@@ -7338,11 +7320,11 @@ wysihtml5.commands = {
7338
7320
  var undef;
7339
7321
 
7340
7322
  wysihtml5.commands.insertHTML = {
7341
- exec: function(element, command, html) {
7342
- if (wysihtml5.commands.support(command)) {
7343
- element.ownerDocument.execCommand(command, false, html);
7323
+ exec: function(composer, command, html) {
7324
+ if (composer.commands.support(command)) {
7325
+ composer.doc.execCommand(command, false, html);
7344
7326
  } else {
7345
- wysihtml5.selection.insertHTML(html);
7327
+ composer.selection.insertHTML(html);
7346
7328
  }
7347
7329
  },
7348
7330
 
@@ -7364,33 +7346,34 @@ wysihtml5.commands = {
7364
7346
  *
7365
7347
  * @example
7366
7348
  * // either ...
7367
- * wysihtml5.commands.insertImage.exec(element, "insertImage", "http://www.google.de/logo.jpg");
7349
+ * wysihtml5.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
7368
7350
  * // ... or ...
7369
- * wysihtml5.commands.insertImage.exec(element, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
7351
+ * wysihtml5.commands.insertImage.exec(composer, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
7370
7352
  */
7371
- exec: function(element, command, value) {
7353
+ exec: function(composer, command, value) {
7372
7354
  value = typeof(value) === "object" ? value : { src: value };
7373
7355
 
7374
- var doc = element.ownerDocument,
7375
- image = this.state(element),
7356
+ var doc = composer.doc,
7357
+ image = this.state(composer),
7358
+ textNode,
7376
7359
  i,
7377
7360
  parent;
7378
7361
 
7379
7362
  if (image) {
7380
7363
  // Image already selected, set the caret before it and delete it
7381
- wysihtml5.selection.setBefore(image);
7364
+ composer.selection.setBefore(image);
7382
7365
  parent = image.parentNode;
7383
7366
  parent.removeChild(image);
7384
7367
 
7385
7368
  // and it's parent <a> too if it hasn't got any other relevant child nodes
7386
7369
  wysihtml5.dom.removeEmptyTextNodes(parent);
7387
7370
  if (parent.nodeName === "A" && !parent.firstChild) {
7388
- wysihtml5.selection.setAfter(parent);
7371
+ composer.selection.setAfter(parent);
7389
7372
  parent.parentNode.removeChild(parent);
7390
7373
  }
7391
7374
 
7392
7375
  // firefox and ie sometimes don't remove the image handles, even though the image got removed
7393
- wysihtml5.quirks.redraw(element);
7376
+ wysihtml5.quirks.redraw(composer.element);
7394
7377
  return;
7395
7378
  }
7396
7379
 
@@ -7400,12 +7383,18 @@ wysihtml5.commands = {
7400
7383
  image[i] = value[i];
7401
7384
  }
7402
7385
 
7403
- wysihtml5.selection.insertNode(image);
7404
- wysihtml5.selection.setAfter(image);
7386
+ composer.selection.insertNode(image);
7387
+ if (wysihtml5.browser.hasProblemsSettingCaretAfterImg()) {
7388
+ textNode = doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
7389
+ composer.selection.insertNode(textNode);
7390
+ composer.selection.setAfter(textNode);
7391
+ } else {
7392
+ composer.selection.setAfter(image);
7393
+ }
7405
7394
  },
7406
7395
 
7407
- state: function(element) {
7408
- var doc = element.ownerDocument,
7396
+ state: function(composer) {
7397
+ var doc = composer.doc,
7409
7398
  selectedNode,
7410
7399
  text,
7411
7400
  imagesInSelection;
@@ -7414,7 +7403,7 @@ wysihtml5.commands = {
7414
7403
  return false;
7415
7404
  }
7416
7405
 
7417
- selectedNode = wysihtml5.selection.getSelectedNode();
7406
+ selectedNode = composer.selection.getSelectedNode();
7418
7407
  if (!selectedNode) {
7419
7408
  return false;
7420
7409
  }
@@ -7428,13 +7417,13 @@ wysihtml5.commands = {
7428
7417
  return false;
7429
7418
  }
7430
7419
 
7431
- text = wysihtml5.selection.getText();
7420
+ text = composer.selection.getText();
7432
7421
  text = wysihtml5.lang.string(text).trim();
7433
7422
  if (text) {
7434
7423
  return false;
7435
7424
  }
7436
7425
 
7437
- imagesInSelection = wysihtml5.selection.getNodes(wysihtml5.ELEMENT_NODE, function(node) {
7426
+ imagesInSelection = composer.selection.getNodes(wysihtml5.ELEMENT_NODE, function(node) {
7438
7427
  return node.nodeName === "IMG";
7439
7428
  });
7440
7429
 
@@ -7445,8 +7434,8 @@ wysihtml5.commands = {
7445
7434
  return imagesInSelection[0];
7446
7435
  },
7447
7436
 
7448
- value: function(element) {
7449
- var image = this.state(element);
7437
+ value: function(composer) {
7438
+ var image = this.state(composer);
7450
7439
  return image && image.src;
7451
7440
  }
7452
7441
  };
@@ -7455,14 +7444,14 @@ wysihtml5.commands = {
7455
7444
  LINE_BREAK = "<br>" + (wysihtml5.browser.needsSpaceAfterLineBreak() ? " " : "");
7456
7445
 
7457
7446
  wysihtml5.commands.insertLineBreak = {
7458
- exec: function(element, command) {
7459
- if (wysihtml5.commands.support(command)) {
7460
- element.ownerDocument.execCommand(command, false, null);
7447
+ exec: function(composer, command) {
7448
+ if (composer.commands.support(command)) {
7449
+ composer.doc.execCommand(command, false, null);
7461
7450
  if (!wysihtml5.browser.autoScrollsToCaret()) {
7462
- wysihtml5.selection.scrollIntoView(element);
7451
+ composer.selection.scrollIntoView();
7463
7452
  }
7464
7453
  } else {
7465
- wysihtml5.commands.exec("insertHTML", LINE_BREAK);
7454
+ composer.commands.exec("insertHTML", LINE_BREAK);
7466
7455
  }
7467
7456
  },
7468
7457
 
@@ -7478,33 +7467,33 @@ wysihtml5.commands = {
7478
7467
  var undef;
7479
7468
 
7480
7469
  wysihtml5.commands.insertOrderedList = {
7481
- exec: function(element, command) {
7482
- var doc = element.ownerDocument,
7470
+ exec: function(composer, command) {
7471
+ var doc = composer.doc,
7483
7472
  selectedNode,
7484
7473
  isEmpty,
7485
7474
  tempElement,
7486
7475
  list;
7487
7476
 
7488
- if (wysihtml5.commands.support(command)) {
7477
+ if (composer.commands.support(command)) {
7489
7478
  doc.execCommand(command, false, null);
7490
7479
  } else {
7491
- selectedNode = wysihtml5.selection.getSelectedNode();
7480
+ selectedNode = composer.selection.getSelectedNode();
7492
7481
  list = wysihtml5.dom.getParentElement(selectedNode, { nodeName: ["UL", "OL"] }, 4);
7493
7482
  if (!list) {
7494
7483
  tempElement = doc.createElement("span");
7495
- wysihtml5.selection.surround(tempElement);
7484
+ composer.selection.surround(tempElement);
7496
7485
  isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
7497
- wysihtml5.selection.executeAndRestoreSimple(function() {
7486
+ composer.selection.executeAndRestoreSimple(function() {
7498
7487
  list = wysihtml5.dom.convertToList(tempElement, "ol");
7499
7488
  });
7500
7489
 
7501
7490
  if (isEmpty) {
7502
- wysihtml5.selection.selectNode(list.querySelector("li"));
7491
+ composer.selection.selectNode(list.querySelector("li"));
7503
7492
  }
7504
7493
  return;
7505
7494
  }
7506
7495
 
7507
- wysihtml5.selection.executeAndRestoreSimple(function() {
7496
+ composer.selection.executeAndRestoreSimple(function() {
7508
7497
  if (list.nodeName === "OL") {
7509
7498
  // Unwrap list
7510
7499
  // <ol><li>foo</li><li>bar</li></ol>
@@ -7522,9 +7511,9 @@ wysihtml5.commands = {
7522
7511
  }
7523
7512
  },
7524
7513
 
7525
- state: function(element, command) {
7514
+ state: function(composer, command) {
7526
7515
  try {
7527
- return element.ownerDocument.queryCommandState(command);
7516
+ return composer.doc.queryCommandState(command);
7528
7517
  } catch(e) {
7529
7518
  return false;
7530
7519
  }
@@ -7538,34 +7527,34 @@ wysihtml5.commands = {
7538
7527
  var undef;
7539
7528
 
7540
7529
  wysihtml5.commands.insertUnorderedList = {
7541
- exec: function(element, command) {
7542
- var doc = element.ownerDocument,
7530
+ exec: function(composer, command) {
7531
+ var doc = composer.doc,
7543
7532
  selectedNode,
7544
7533
  isEmpty,
7545
7534
  tempElement,
7546
7535
  list;
7547
7536
 
7548
- if (wysihtml5.commands.support(command)) {
7537
+ if (composer.commands.support(command)) {
7549
7538
  doc.execCommand(command, false, null);
7550
7539
  } else {
7551
- selectedNode = wysihtml5.selection.getSelectedNode();
7540
+ selectedNode = composer.selection.getSelectedNode();
7552
7541
  list = wysihtml5.dom.getParentElement(selectedNode, { nodeName: ["UL", "OL"] });
7553
7542
 
7554
7543
  if (!list) {
7555
7544
  tempElement = doc.createElement("span");
7556
- wysihtml5.selection.surround(tempElement);
7545
+ composer.selection.surround(tempElement);
7557
7546
  isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
7558
- wysihtml5.selection.executeAndRestoreSimple(function() {
7547
+ composer.selection.executeAndRestoreSimple(function() {
7559
7548
  list = wysihtml5.dom.convertToList(tempElement, "ul");
7560
7549
  });
7561
7550
 
7562
7551
  if (isEmpty) {
7563
- wysihtml5.selection.selectNode(list.querySelector("li"));
7552
+ composer.selection.selectNode(list.querySelector("li"));
7564
7553
  }
7565
7554
  return;
7566
7555
  }
7567
7556
 
7568
- wysihtml5.selection.executeAndRestoreSimple(function() {
7557
+ composer.selection.executeAndRestoreSimple(function() {
7569
7558
  if (list.nodeName === "UL") {
7570
7559
  // Unwrap list
7571
7560
  // <ul><li>foo</li><li>bar</li></ul>
@@ -7583,9 +7572,9 @@ wysihtml5.commands = {
7583
7572
  }
7584
7573
  },
7585
7574
 
7586
- state: function(element, command) {
7575
+ state: function(composer, command) {
7587
7576
  try {
7588
- return element.ownerDocument.queryCommandState(command);
7577
+ return composer.doc.queryCommandState(command);
7589
7578
  } catch(e) {
7590
7579
  return false;
7591
7580
  }
@@ -7599,17 +7588,17 @@ wysihtml5.commands = {
7599
7588
  var undef;
7600
7589
 
7601
7590
  wysihtml5.commands.italic = {
7602
- exec: function(element, command) {
7603
- return wysihtml5.commands.formatInline.exec(element, command, "i");
7591
+ exec: function(composer, command) {
7592
+ return wysihtml5.commands.formatInline.exec(composer, command, "i");
7604
7593
  },
7605
7594
 
7606
- state: function(element, command, color) {
7595
+ state: function(composer, command, color) {
7607
7596
  // element.ownerDocument.queryCommandState("italic") results:
7608
7597
  // firefox: only <i>
7609
7598
  // chrome: <i>, <em>, <blockquote>, ...
7610
7599
  // ie: <i>, <em>
7611
7600
  // opera: only <i>
7612
- return wysihtml5.commands.formatInline.state(element, command, "i");
7601
+ return wysihtml5.commands.formatInline.state(composer, command, "i");
7613
7602
  },
7614
7603
 
7615
7604
  value: function() {
@@ -7622,12 +7611,12 @@ wysihtml5.commands = {
7622
7611
  REG_EXP = /wysiwyg-text-align-[a-z]+/g;
7623
7612
 
7624
7613
  wysihtml5.commands.justifyCenter = {
7625
- exec: function(element, command) {
7626
- return wysihtml5.commands.formatBlock.exec(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7614
+ exec: function(composer, command) {
7615
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7627
7616
  },
7628
7617
 
7629
- state: function(element, command) {
7630
- return wysihtml5.commands.formatBlock.state(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7618
+ state: function(composer, command) {
7619
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7631
7620
  },
7632
7621
 
7633
7622
  value: function() {
@@ -7640,12 +7629,12 @@ wysihtml5.commands = {
7640
7629
  REG_EXP = /wysiwyg-text-align-[a-z]+/g;
7641
7630
 
7642
7631
  wysihtml5.commands.justifyLeft = {
7643
- exec: function(element, command) {
7644
- return wysihtml5.commands.formatBlock.exec(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7632
+ exec: function(composer, command) {
7633
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7645
7634
  },
7646
7635
 
7647
- state: function(element, command) {
7648
- return wysihtml5.commands.formatBlock.state(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7636
+ state: function(composer, command) {
7637
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7649
7638
  },
7650
7639
 
7651
7640
  value: function() {
@@ -7658,12 +7647,12 @@ wysihtml5.commands = {
7658
7647
  REG_EXP = /wysiwyg-text-align-[a-z]+/g;
7659
7648
 
7660
7649
  wysihtml5.commands.justifyRight = {
7661
- exec: function(element, command) {
7662
- return wysihtml5.commands.formatBlock.exec(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7650
+ exec: function(composer, command) {
7651
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7663
7652
  },
7664
7653
 
7665
- state: function(element, command) {
7666
- return wysihtml5.commands.formatBlock.state(element, "formatBlock", null, CLASS_NAME, REG_EXP);
7654
+ state: function(composer, command) {
7655
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
7667
7656
  },
7668
7657
 
7669
7658
  value: function() {
@@ -7676,12 +7665,12 @@ wysihtml5.commands = {
7676
7665
  CLASS_NAME = "wysiwyg-text-decoration-underline";
7677
7666
 
7678
7667
  wysihtml5.commands.underline = {
7679
- exec: function(element, command) {
7680
- return wysihtml5.commands.formatInline.exec(element, command, "span", CLASS_NAME, REG_EXP);
7668
+ exec: function(composer, command) {
7669
+ return wysihtml5.commands.formatInline.exec(composer, command, "span", CLASS_NAME, REG_EXP);
7681
7670
  },
7682
7671
 
7683
- state: function(element, command) {
7684
- return wysihtml5.commands.formatInline.state(element, command, "span", CLASS_NAME, REG_EXP);
7672
+ state: function(composer, command) {
7673
+ return wysihtml5.commands.formatInline.state(composer, command, "span", CLASS_NAME, REG_EXP);
7685
7674
  },
7686
7675
 
7687
7676
  value: function() {
@@ -7713,24 +7702,25 @@ wysihtml5.commands = {
7713
7702
  /** @scope wysihtml5.UndoManager.prototype */ {
7714
7703
  constructor: function(editor) {
7715
7704
  this.editor = editor;
7716
- this.composerElement = editor.composer.element;
7717
- this.history = [this.editor.composer.getValue()];
7705
+ this.composer = editor.composer;
7706
+ this.element = this.composer.element;
7707
+ this.history = [this.composer.getValue()];
7718
7708
  this.position = 1;
7719
7709
 
7720
7710
  // Undo manager currently only supported in browsers who have the insertHTML command (not IE)
7721
- if (wysihtml5.commands.support("insertHTML")) {
7711
+ if (this.composer.commands.support("insertHTML")) {
7722
7712
  this._observe();
7723
7713
  }
7724
7714
  },
7725
7715
 
7726
7716
  _observe: function() {
7727
- var that = this,
7728
- doc = this.editor.composer.sandbox.getDocument(),
7717
+ var that = this,
7718
+ doc = this.composer.sandbox.getDocument(),
7729
7719
  lastKey;
7730
7720
 
7731
7721
  // Catch CTRL+Z and CTRL+Y
7732
- dom.observe(this.composerElement, "keydown", function(event) {
7733
- if (!event.ctrlKey && !event.metaKey) {
7722
+ dom.observe(this.element, "keydown", function(event) {
7723
+ if (event.altKey || (!event.ctrlKey && !event.metaKey)) {
7734
7724
  return;
7735
7725
  }
7736
7726
 
@@ -7748,7 +7738,7 @@ wysihtml5.commands = {
7748
7738
  });
7749
7739
 
7750
7740
  // Catch delete and backspace
7751
- dom.observe(this.composerElement, "keydown", function(event) {
7741
+ dom.observe(this.element, "keydown", function(event) {
7752
7742
  var keyCode = event.keyCode;
7753
7743
  if (keyCode === lastKey) {
7754
7744
  return;
@@ -7761,12 +7751,6 @@ wysihtml5.commands = {
7761
7751
  }
7762
7752
  });
7763
7753
 
7764
- var interval, observed, cleanUp = function() {
7765
- cleanTempElements(doc);
7766
- clearInterval(interval);
7767
- };
7768
-
7769
-
7770
7754
  // Now this is very hacky:
7771
7755
  // These days browsers don't offer a undo/redo event which we could hook into
7772
7756
  // to be notified when the user hits undo/redo in the contextmenu.
@@ -7774,36 +7758,43 @@ wysihtml5.commands = {
7774
7758
  // The last element being inserted will be immediately be removed again by a exexCommand("undo")
7775
7759
  // => When the second element appears in the dom tree then we know the user clicked "redo" in the context menu
7776
7760
  // => When the first element disappears from the dom tree then we know the user clicked "undo" in the context menu
7777
- dom.observe(this.composerElement, "contextmenu", function() {
7778
- cleanUp();
7779
- wysihtml5.selection.executeAndRestoreSimple(function() {
7780
- if (that.composerElement.lastChild) {
7781
- wysihtml5.selection.setAfter(that.composerElement.lastChild);
7782
- }
7783
-
7784
- // enable undo button in context menu
7785
- doc.execCommand("insertHTML", false, UNDO_HTML);
7786
- // enable redo button in context menu
7787
- doc.execCommand("insertHTML", false, REDO_HTML);
7788
- doc.execCommand("undo", false, null);
7789
- });
7761
+ if (wysihtml5.browser.hasUndoInContextMenu()) {
7762
+ var interval, observed, cleanUp = function() {
7763
+ cleanTempElements(doc);
7764
+ clearInterval(interval);
7765
+ };
7790
7766
 
7791
- interval = setInterval(function() {
7792
- if (doc.getElementById("_wysihtml5-redo")) {
7793
- cleanUp();
7794
- that.redo();
7795
- } else if (!doc.getElementById("_wysihtml5-undo")) {
7796
- cleanUp();
7797
- that.undo();
7767
+ dom.observe(this.element, "contextmenu", function() {
7768
+ cleanUp();
7769
+ that.composer.selection.executeAndRestoreSimple(function() {
7770
+ if (that.element.lastChild) {
7771
+ that.composer.selection.setAfter(that.element.lastChild);
7772
+ }
7773
+
7774
+ // enable undo button in context menu
7775
+ doc.execCommand("insertHTML", false, UNDO_HTML);
7776
+ // enable redo button in context menu
7777
+ doc.execCommand("insertHTML", false, REDO_HTML);
7778
+ doc.execCommand("undo", false, null);
7779
+ });
7780
+
7781
+ interval = setInterval(function() {
7782
+ if (doc.getElementById("_wysihtml5-redo")) {
7783
+ cleanUp();
7784
+ that.redo();
7785
+ } else if (!doc.getElementById("_wysihtml5-undo")) {
7786
+ cleanUp();
7787
+ that.undo();
7788
+ }
7789
+ }, 400);
7790
+
7791
+ if (!observed) {
7792
+ observed = true;
7793
+ dom.observe(document, "mousedown", cleanUp);
7794
+ dom.observe(doc, ["mousedown", "paste", "cut", "copy"], cleanUp);
7798
7795
  }
7799
- }, 400);
7800
-
7801
- if (!observed) {
7802
- observed = true;
7803
- dom.observe(document, "mousedown", cleanUp);
7804
- dom.observe(doc, ["mousedown", "paste", "cut", "copy"], cleanUp);
7805
- }
7806
- });
7796
+ });
7797
+ }
7807
7798
 
7808
7799
  this.editor
7809
7800
  .observe("newword:composer", function() {
@@ -7817,7 +7808,7 @@ wysihtml5.commands = {
7817
7808
 
7818
7809
  transact: function() {
7819
7810
  var previousHtml = this.history[this.position - 1],
7820
- currentHtml = this.editor.composer.getValue();
7811
+ currentHtml = this.composer.getValue();
7821
7812
 
7822
7813
  if (currentHtml == previousHtml) {
7823
7814
  return;
@@ -7854,7 +7845,7 @@ wysihtml5.commands = {
7854
7845
  },
7855
7846
 
7856
7847
  set: function(html) {
7857
- this.editor.composer.setValue(html);
7848
+ this.composer.setValue(html);
7858
7849
  this.editor.focus(true);
7859
7850
  }
7860
7851
  });
@@ -7913,9 +7904,7 @@ wysihtml5.views.View = Base.extend(
7913
7904
  }
7914
7905
  });(function(wysihtml5) {
7915
7906
  var dom = wysihtml5.dom,
7916
- browser = wysihtml5.browser,
7917
- selection = wysihtml5.selection,
7918
- commands = wysihtml5.commands;
7907
+ browser = wysihtml5.browser;
7919
7908
 
7920
7909
  wysihtml5.views.Composer = wysihtml5.views.View.extend(
7921
7910
  /** @scope wysihtml5.views.Composer.prototype */ {
@@ -7983,14 +7972,21 @@ wysihtml5.views.View = Base.extend(
7983
7972
  },
7984
7973
 
7985
7974
  focus: function(setToEnd) {
7975
+ // IE 8 fires the focus event after .focus()
7976
+ // This is needed by our simulate_placeholder.js to work
7977
+ // therefore we clear it ourselves this time
7978
+ if (wysihtml5.browser.doesAsyncFocus() && this.hasPlaceholderSet()) {
7979
+ this.clear();
7980
+ }
7981
+
7986
7982
  this.base();
7987
7983
 
7988
7984
  var lastChild = this.element.lastChild;
7989
7985
  if (setToEnd && lastChild) {
7990
7986
  if (lastChild.nodeName === "BR") {
7991
- selection.setBefore(this.element.lastChild);
7987
+ this.selection.setBefore(this.element.lastChild);
7992
7988
  } else {
7993
- selection.setAfter(this.element.lastChild);
7989
+ this.selection.setAfter(this.element.lastChild);
7994
7990
  }
7995
7991
  }
7996
7992
  },
@@ -8018,8 +8014,7 @@ wysihtml5.views.View = Base.extend(
8018
8014
  this.sandbox = new dom.Sandbox(function() {
8019
8015
  that._create();
8020
8016
  }, {
8021
- stylesheets: this.config.stylesheets,
8022
- uaCompatible: "IE=7"
8017
+ stylesheets: this.config.stylesheets
8023
8018
  });
8024
8019
  this.iframe = this.sandbox.getIframe();
8025
8020
 
@@ -8038,16 +8033,17 @@ wysihtml5.views.View = Base.extend(
8038
8033
  _create: function() {
8039
8034
  var that = this;
8040
8035
 
8041
- this.element = this.sandbox.getDocument().body;
8036
+ this.doc = this.sandbox.getDocument();
8037
+ this.element = this.doc.body;
8042
8038
  this.textarea = this.parent.textarea;
8043
8039
  this.element.innerHTML = this.textarea.getValue(true);
8044
8040
  this.enable();
8045
8041
 
8046
8042
  // Make sure our selection handler is ready
8047
- selection.initialize(this.sandbox.getDocument());
8043
+ this.selection = new wysihtml5.Selection(this.parent);
8048
8044
 
8049
- // Make sure commands are ready
8050
- commands.initialize(this.parent);
8045
+ // Make sure commands dispatcher is ready
8046
+ this.commands = new wysihtml5.Commands(this.parent);
8051
8047
 
8052
8048
  dom.copyAttributes([
8053
8049
  "className", "spellcheck", "title", "lang", "dir", "accessKey"
@@ -8077,7 +8073,7 @@ wysihtml5.views.View = Base.extend(
8077
8073
  }
8078
8074
 
8079
8075
  // Make sure that the browser avoids using inline styles whenever possible
8080
- commands.exec("styleWithCSS", false);
8076
+ this.commands.exec("styleWithCSS", false);
8081
8077
 
8082
8078
  this._initAutoLinking();
8083
8079
  this._initObjectResizing();
@@ -8088,15 +8084,15 @@ wysihtml5.views.View = Base.extend(
8088
8084
  setTimeout(function() { that.focus(); }, 100);
8089
8085
  }
8090
8086
 
8091
- wysihtml5.quirks.insertLineBreakOnReturn(this.element);
8087
+ wysihtml5.quirks.insertLineBreakOnReturn(this);
8092
8088
 
8093
8089
  // IE sometimes leaves a single paragraph, which can't be removed by the user
8094
8090
  if (!browser.clearsContentEditableCorrectly()) {
8095
- wysihtml5.quirks.ensureProperClearing(this.element);
8091
+ wysihtml5.quirks.ensureProperClearing(this);
8096
8092
  }
8097
8093
 
8098
8094
  if (!browser.clearsListsInContentEditableCorrectly()) {
8099
- wysihtml5.quirks.ensureProperClearingOfLists(this.element);
8095
+ wysihtml5.quirks.ensureProperClearingOfLists(this);
8100
8096
  }
8101
8097
 
8102
8098
  // Set up a sync that makes sure that textarea and editor have the same content
@@ -8112,10 +8108,11 @@ wysihtml5.views.View = Base.extend(
8112
8108
  },
8113
8109
 
8114
8110
  _initAutoLinking: function() {
8115
- var supportsDisablingOfAutoLinking = browser.canDisableAutoLinking(),
8111
+ var that = this,
8112
+ supportsDisablingOfAutoLinking = browser.canDisableAutoLinking(),
8116
8113
  supportsAutoLinking = browser.doesAutoLinkingInContentEditable();
8117
8114
  if (supportsDisablingOfAutoLinking) {
8118
- commands.exec("autoUrlDetect", false);
8115
+ this.commands.exec("autoUrlDetect", false);
8119
8116
  }
8120
8117
 
8121
8118
  if (!this.config.autoLink) {
@@ -8126,7 +8123,7 @@ wysihtml5.views.View = Base.extend(
8126
8123
  // OR when he supports auto linking but we were able to turn it off (IE9+)
8127
8124
  if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
8128
8125
  this.parent.observe("newword:composer", function() {
8129
- selection.executeAndRestore(function(startContainer, endContainer) {
8126
+ that.selection.executeAndRestore(function(startContainer, endContainer) {
8130
8127
  dom.autoLink(endContainer.parentNode);
8131
8128
  });
8132
8129
  });
@@ -8153,7 +8150,7 @@ wysihtml5.views.View = Base.extend(
8153
8150
  return;
8154
8151
  }
8155
8152
 
8156
- var selectedNode = selection.getSelectedNode(event.target.ownerDocument),
8153
+ var selectedNode = that.selection.getSelectedNode(event.target.ownerDocument),
8157
8154
  link = dom.getParentElement(selectedNode, { nodeName: "A" }, 4),
8158
8155
  textContent;
8159
8156
 
@@ -8183,8 +8180,8 @@ wysihtml5.views.View = Base.extend(
8183
8180
  propertiesLength = properties.length,
8184
8181
  element = this.element;
8185
8182
 
8186
- commands.exec("enableObjectResizing", this.config.allowObjectResizing);
8187
-
8183
+ this.commands.exec("enableObjectResizing", this.config.allowObjectResizing);
8184
+
8188
8185
  if (this.config.allowObjectResizing) {
8189
8186
  // IE sets inline styles after resizing objects
8190
8187
  // The following lines make sure that the width/height css properties
@@ -8406,8 +8403,6 @@ wysihtml5.views.View = Base.extend(
8406
8403
  * - Catch paste events
8407
8404
  * - Dispatch proprietary newword:composer event
8408
8405
  * - Keyboard shortcuts
8409
- *
8410
- * @author Christopher Blum <christopher.blum@xing.com>
8411
8406
  */
8412
8407
  (function(wysihtml5) {
8413
8408
  var dom = wysihtml5.dom,
@@ -8470,7 +8465,7 @@ wysihtml5.views.View = Base.extend(
8470
8465
  originalScrollTop = document.documentElement.scrollTop || document.body.scrollTop,
8471
8466
  originalScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
8472
8467
  try {
8473
- wysihtml5.selection.insertNode(input);
8468
+ that.selection.insertNode(input);
8474
8469
  } catch(e) {
8475
8470
  element.appendChild(input);
8476
8471
  }
@@ -8501,7 +8496,7 @@ wysihtml5.views.View = Base.extend(
8501
8496
  }
8502
8497
  if (data) {
8503
8498
  element.focus();
8504
- wysihtml5.commands.exec("insertHTML", data);
8499
+ that.commands.exec("insertHTML", data);
8505
8500
  that.parent.fire("paste").fire("paste:composer");
8506
8501
  event.stopPropagation();
8507
8502
  event.preventDefault();
@@ -8529,7 +8524,7 @@ wysihtml5.views.View = Base.extend(
8529
8524
  dom.observe(element, "mousedown", function(event) {
8530
8525
  var target = event.target;
8531
8526
  if (target.nodeName === "IMG") {
8532
- wysihtml5.selection.selectNode(target);
8527
+ that.selection.selectNode(target);
8533
8528
  event.preventDefault();
8534
8529
  }
8535
8530
  });
@@ -8540,14 +8535,14 @@ wysihtml5.views.View = Base.extend(
8540
8535
  var keyCode = event.keyCode,
8541
8536
  command = shortcuts[keyCode];
8542
8537
  if ((event.ctrlKey || event.metaKey) && command) {
8543
- wysihtml5.commands.exec(command);
8538
+ that.commands.exec(command);
8544
8539
  event.preventDefault();
8545
8540
  }
8546
8541
  });
8547
8542
 
8548
8543
  // --------- Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor ---------
8549
8544
  dom.observe(element, "keydown", function(event) {
8550
- var target = wysihtml5.selection.getSelectedNode(true),
8545
+ var target = that.selection.getSelectedNode(true),
8551
8546
  keyCode = event.keyCode,
8552
8547
  parent;
8553
8548
  if (target && target.nodeName === "IMG" && (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY)) { // 8 => backspace, 46 => delete
@@ -8747,7 +8742,6 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
8747
8742
  });/**
8748
8743
  * Toolbar Dialog
8749
8744
  *
8750
- * @author Christopher Blum <christopher.blum@xing.com>
8751
8745
  * @param {Element} link The toolbar link which causes the dialog to show up
8752
8746
  * @param {Element} container The dialog container
8753
8747
  *
@@ -8877,7 +8871,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
8877
8871
  * Basically it adopted the attribute values into the corresponding input fields
8878
8872
  *
8879
8873
  */
8880
- _interpolate: function() {
8874
+ _interpolate: function(avoidHiddenFields) {
8881
8875
  var field,
8882
8876
  fieldName,
8883
8877
  newValue,
@@ -8887,10 +8881,18 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
8887
8881
  i = 0;
8888
8882
  for (; i<length; i++) {
8889
8883
  field = fields[i];
8884
+
8890
8885
  // Never change elements where the user is currently typing in
8891
8886
  if (field === focusedElement) {
8892
8887
  continue;
8893
8888
  }
8889
+
8890
+ // Don't update hidden fields
8891
+ // See https://github.com/xing/wysihtml5/pull/14
8892
+ if (avoidHiddenFields && field.type === "hidden") {
8893
+ continue;
8894
+ }
8895
+
8894
8896
  fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
8895
8897
  newValue = this.elementToChange ? (this.elementToChange[fieldName] || "") : field.defaultValue;
8896
8898
  field.value = newValue;
@@ -8907,7 +8909,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
8907
8909
  this._observe();
8908
8910
  this._interpolate();
8909
8911
  if (elementToChange) {
8910
- this.interval = setInterval(function() { that._interpolate(); }, 500);
8912
+ this.interval = setInterval(function() { that._interpolate(true); }, 500);
8911
8913
  }
8912
8914
  dom.addClass(this.link, CLASS_NAME_OPENED);
8913
8915
  this.container.style.display = "";
@@ -8943,8 +8945,6 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
8943
8945
  *
8944
8946
  * "Accessing Google Speech API Chrome 11"
8945
8947
  * http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
8946
- *
8947
- * @author Christopher Blum <christopher.blum@xing.com>
8948
8948
  */
8949
8949
  (function(wysihtml5) {
8950
8950
  var dom = wysihtml5.dom;
@@ -9021,7 +9021,6 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9021
9021
  })(wysihtml5);/**
9022
9022
  * Toolbar
9023
9023
  *
9024
- * @author Christopher Blum <christopher.blum@xing.com>
9025
9024
  * @param {Object} parent Reference to instance of Editor instance
9026
9025
  * @param {Element} container Reference to the toolbar container element
9027
9026
  *
@@ -9092,20 +9091,19 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9092
9091
  dialogElement = this.container.querySelector("[data-wysihtml5-dialog='" + command + "']"),
9093
9092
  dialog,
9094
9093
  caretBookmark;
9094
+
9095
9095
  if (dialogElement) {
9096
9096
  dialog = new wysihtml5.toolbar.Dialog(link, dialogElement);
9097
9097
 
9098
9098
  dialog.observe("show", function() {
9099
- caretBookmark = wysihtml5.selection.getBookmark();
9099
+ caretBookmark = that.composer.selection.getBookmark();
9100
9100
 
9101
9101
  that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
9102
9102
  });
9103
9103
 
9104
9104
  dialog.observe("save", function(attributes) {
9105
- that.editor.focus(false);
9106
-
9107
9105
  if (caretBookmark) {
9108
- wysihtml5.selection.setBookmark(caretBookmark);
9106
+ that.composer.selection.setBookmark(caretBookmark);
9109
9107
  }
9110
9108
  that._execCommand(command, attributes);
9111
9109
 
@@ -9145,7 +9143,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9145
9143
  // Make sure that composer is focussed (false => don't move caret to the end)
9146
9144
  this.editor.focus(false);
9147
9145
 
9148
- wysihtml5.commands.exec(command, commandValue);
9146
+ this.composer.commands.exec(command, commandValue);
9149
9147
  this._updateLinkStates();
9150
9148
  },
9151
9149
 
@@ -9240,7 +9238,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9240
9238
  command.dialog.hide();
9241
9239
  }
9242
9240
  } else {
9243
- state = wysihtml5.commands.state(command.name, command.value);
9241
+ state = this.composer.commands.state(command.name, command.value);
9244
9242
  if (wysihtml5.lang.object(state).isArray()) {
9245
9243
  // Grab first and only object/element in state array, otherwise convert state into boolean
9246
9244
  // to avoid showing a dialog for multiple selected elements which may have different attributes
@@ -9287,7 +9285,6 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9287
9285
  /**
9288
9286
  * WYSIHTML5 Editor
9289
9287
  *
9290
- * @author Christopher Blum <christopher.blum@xing.com>
9291
9288
  * @param {Element} textareaElement Reference to the textarea which should be turned into a rich text interface
9292
9289
  * @param {Object} [config] See defaultConfig object below for explanation of each individual config option
9293
9290
  *
@@ -9449,7 +9446,7 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend(
9449
9446
  this.observe("paste:composer", function() {
9450
9447
  var keepScrollPosition = true,
9451
9448
  that = this;
9452
- wysihtml5.selection.executeAndRestore(function() {
9449
+ that.composer.selection.executeAndRestore(function() {
9453
9450
  wysihtml5.quirks.cleanPastedHTML(that.composer.element);
9454
9451
  that.parse(that.composer.element);
9455
9452
  }, keepScrollPosition);