wysihtml-rails 0.5.0.beta6 → 0.5.0.beta7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 169166e826e5cf81830b93add509b5443904cdbb
4
- data.tar.gz: b3576b0d1ad6ac31988caa0e25e41e6920fb9e02
3
+ metadata.gz: 64f3d46ae259b28d6540ada222ddc2254e450305
4
+ data.tar.gz: b6124307107e3a712ad473e5461da9ca128c1e8d
5
5
  SHA512:
6
- metadata.gz: 56d0909357b5235b426c651ce8119d86f4a50d431e5e3b9725f511d1afe6663d310f01407f61da69beb4196d0bf196cd82a235ce510ebf7d12826d349fa7f025
7
- data.tar.gz: 21b029d33b26b2ea0b43078dae432823a3eff5937526f648e430d611dbc389ce3af640573d4dd4d798e7d06e834877d8405997853cea7192c0a9be9725b5345f
6
+ metadata.gz: d741717b1e1b496c1f3073f6b7a1ed16bd891ef69d5712342a860befc67880cef51e5e630dbd7cea70f48d7517404d065ceabb4671b0863e9a3e8088c44d8890
7
+ data.tar.gz: 1eca60fa61f5ef49a5e21873ff6a5610622b059a1e06687c0776215e489d62344fc9c43d716ff1d1343ce8bdbeca8ae880aaebeb4683948031587f8da3732dcd
@@ -1,5 +1,5 @@
1
1
  module Wysihtml
2
2
  module Rails
3
- VERSION = "0.5.0.beta6"
3
+ VERSION = "0.5.0.beta7"
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license wysihtml5x v0.5.0-beta6
3
- * https://github.com/Edicy/wysihtml5
2
+ * @license wysihtml v0.5.0-beta7
3
+ * https://github.com/Voog/wysihtml
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
6
6
  * Secondary author of extended features: Oliver Pulges (https://github.com/pulges)
@@ -10,7 +10,7 @@
10
10
  *
11
11
  */
12
12
  var wysihtml5 = {
13
- version: "0.5.0-beta6",
13
+ version: "0.5.0-beta7",
14
14
 
15
15
  // namespaces
16
16
  commands: {},
@@ -5081,9 +5081,17 @@ wysihtml5.browser = (function() {
5081
5081
  * wysihtml5.lang.object({ foo: 1, bar: 1 }).merge({ bar: 2, baz: 3 }).get();
5082
5082
  * // => { foo: 1, bar: 2, baz: 3 }
5083
5083
  */
5084
- merge: function(otherObj) {
5084
+ merge: function(otherObj, deep) {
5085
5085
  for (var i in otherObj) {
5086
- obj[i] = otherObj[i];
5086
+ if (deep && wysihtml5.lang.object(otherObj[i]).isPlainObject() && (typeof obj[i] === "undefined" || wysihtml5.lang.object(obj[i]).isPlainObject())) {
5087
+ if (typeof obj[i] === "undefined") {
5088
+ obj[i] = wysihtml5.lang.object(otherObj[i]).clone(true);
5089
+ } else {
5090
+ wysihtml5.lang.object(obj[i]).merge(wysihtml5.lang.object(otherObj[i]).clone(true));
5091
+ }
5092
+ } else {
5093
+ obj[i] = wysihtml5.lang.object(otherObj[i]).isPlainObject() ? wysihtml5.lang.object(otherObj[i]).clone(true) : otherObj[i];
5094
+ }
5087
5095
  }
5088
5096
  return this;
5089
5097
  },
@@ -5138,7 +5146,7 @@ wysihtml5.browser = (function() {
5138
5146
  },
5139
5147
 
5140
5148
  isPlainObject: function () {
5141
- return Object.prototype.toString.call(obj) === '[object Object]';
5149
+ return obj && Object.prototype.toString.call(obj) === '[object Object]';
5142
5150
  }
5143
5151
  };
5144
5152
  };
@@ -6696,11 +6704,9 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6696
6704
  newAttributeValue;
6697
6705
 
6698
6706
  if (method) {
6699
- if (attributeValue || (attributeName === "alt" && nodeName == "IMG")) {
6700
- newAttributeValue = method(attributeValue);
6701
- if (typeof(newAttributeValue) === "string") {
6702
- return newAttributeValue;
6703
- }
6707
+ newAttributeValue = method(attributeValue, nodeName);
6708
+ if (typeof(newAttributeValue) === "string") {
6709
+ return newAttributeValue;
6704
6710
  }
6705
6711
  }
6706
6712
 
@@ -6935,9 +6941,13 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6935
6941
 
6936
6942
  alt: (function() {
6937
6943
  var REG_EXP = /[^ a-z0-9_\-]/gi;
6938
- return function(attributeValue) {
6944
+ return function(attributeValue, nodeName) {
6939
6945
  if (!attributeValue) {
6940
- return "";
6946
+ if (nodeName === "IMG") {
6947
+ return "";
6948
+ } else {
6949
+ return null;
6950
+ }
6941
6951
  }
6942
6952
  return attributeValue.replace(REG_EXP, "");
6943
6953
  };
@@ -6963,6 +6973,9 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6963
6973
 
6964
6974
  any: (function() {
6965
6975
  return function(attributeValue) {
6976
+ if (!attributeValue) {
6977
+ return null;
6978
+ }
6966
6979
  return attributeValue;
6967
6980
  };
6968
6981
  })()
@@ -7320,6 +7333,9 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7320
7333
  constructor: function(readyCallback, config) {
7321
7334
  this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
7322
7335
  this.config = wysihtml5.lang.object({}).merge(config).get();
7336
+ if (!this.config.className) {
7337
+ this.config.className = "wysihtml5-sandbox";
7338
+ }
7323
7339
  this.editableArea = this._createIframe();
7324
7340
  },
7325
7341
 
@@ -7374,7 +7390,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7374
7390
  _createIframe: function() {
7375
7391
  var that = this,
7376
7392
  iframe = doc.createElement("iframe");
7377
- iframe.className = "wysihtml5-sandbox";
7393
+ iframe.className = this.config.className;
7378
7394
  wysihtml5.dom.setAttributes({
7379
7395
  "security": "restricted",
7380
7396
  "allowtransparency": "true",
@@ -7537,6 +7553,9 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7537
7553
  constructor: function(readyCallback, config, contentEditable) {
7538
7554
  this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
7539
7555
  this.config = wysihtml5.lang.object({}).merge(config).get();
7556
+ if (!this.config.className) {
7557
+ this.config.className = "wysihtml5-sandbox";
7558
+ }
7540
7559
  if (contentEditable) {
7541
7560
  this.element = this._bindElement(contentEditable);
7542
7561
  } else {
@@ -7547,7 +7566,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7547
7566
  // creates a new contenteditable and initiates it
7548
7567
  _createElement: function() {
7549
7568
  var element = doc.createElement("div");
7550
- element.className = "wysihtml5-sandbox";
7569
+ element.className = this.config.className;
7551
7570
  this._loadElement(element);
7552
7571
  return element;
7553
7572
  },
@@ -7626,8 +7645,8 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7626
7645
  * wysihtml.dom.simulatePlaceholder(this, composer, "Foobar");
7627
7646
  */
7628
7647
  (function(dom) {
7629
- dom.simulatePlaceholder = function(editor, view, placeholderText) {
7630
- var CLASS_NAME = "placeholder",
7648
+ dom.simulatePlaceholder = function(editor, view, placeholderText, placeholderClassName) {
7649
+ var CLASS_NAME = placeholderClassName || "wysihtml5-placeholder",
7631
7650
  unset = function() {
7632
7651
  var composerIsVisible = view.element.offsetWidth > 0 && view.element.offsetHeight > 0;
7633
7652
  if (view.hasPlaceholderSet()) {
@@ -11355,7 +11374,7 @@ wysihtml5.Commands = Base.extend(
11355
11374
  function cleanup(composer) {
11356
11375
  var container = composer.element,
11357
11376
  allElements = container.querySelectorAll(BLOCK_ELEMENTS),
11358
- uneditables = container.querySelectorAll(composer.config.uneditableContainerClassname),
11377
+ uneditables = container.querySelectorAll(composer.config.classNames.uneditableContainer),
11359
11378
  elements = wysihtml5.lang.array(allElements).without(uneditables);
11360
11379
 
11361
11380
  for (var i = elements.length; i--;) {
@@ -11627,7 +11646,7 @@ wysihtml5.Commands = Base.extend(
11627
11646
  state = this.state(composer, command, options);
11628
11647
  if (state) {
11629
11648
  bookmark = rangy.saveSelection(composer.win);
11630
- for (var j in state) {
11649
+ for (var j = 0, jmax = state.length; j < jmax; j++) {
11631
11650
  removeOptionsFromElement(state[j], options, composer);
11632
11651
  }
11633
11652
  }
@@ -11650,7 +11669,7 @@ wysihtml5.Commands = Base.extend(
11650
11669
  composer.selection.selectLine();
11651
11670
  }
11652
11671
  }
11653
-
11672
+
11654
11673
  // And get all selection ranges of current composer and iterat
11655
11674
  ranges = composer.selection.getOwnRanges();
11656
11675
  for (var i = ranges.length; i--;) {
@@ -12212,7 +12231,7 @@ wysihtml5.Commands = Base.extend(
12212
12231
 
12213
12232
  if (tempElement) {
12214
12233
  isEmpty = wysihtml5.lang.array(["", "<br>", wysihtml5.INVISIBLE_SPACE]).contains(tempElement.innerHTML);
12215
- list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.uneditableContainerClassname);
12234
+ list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.classNames.uneditableContainer);
12216
12235
  if (isEmpty) {
12217
12236
  composer.selection.selectNode(list.querySelector("li"), true);
12218
12237
  }
@@ -12441,7 +12460,7 @@ wysihtml5.Commands = Base.extend(
12441
12460
  for (row = 0; row < value.rows; row ++) {
12442
12461
  html += '<tr>';
12443
12462
  for (col = 0; col < value.cols; col ++) {
12444
- html += "<td></td>";
12463
+ html += "<td><br></td>";
12445
12464
  }
12446
12465
  html += '</tr>';
12447
12466
  }
@@ -13123,14 +13142,17 @@ wysihtml5.views.View = Base.extend(
13123
13142
 
13124
13143
  _initContentEditableArea: function() {
13125
13144
  var that = this;
13126
-
13127
13145
  if (this.config.noTextarea) {
13128
13146
  this.sandbox = new dom.ContentEditableArea(function() {
13129
13147
  that._create();
13130
- }, {}, this.editableArea);
13148
+ }, {
13149
+ className: this.config.classNames.sandbox
13150
+ }, this.editableArea);
13131
13151
  } else {
13132
13152
  this.sandbox = new dom.ContentEditableArea(function() {
13133
13153
  that._create();
13154
+ }, {
13155
+ className: this.config.classNames.sandbox
13134
13156
  });
13135
13157
  this.editableArea = this.sandbox.getContentEditable();
13136
13158
  dom.insert(this.editableArea).after(this.textarea.element);
@@ -13140,11 +13162,11 @@ wysihtml5.views.View = Base.extend(
13140
13162
 
13141
13163
  _initSandbox: function() {
13142
13164
  var that = this;
13143
-
13144
13165
  this.sandbox = new dom.Sandbox(function() {
13145
13166
  that._create();
13146
13167
  }, {
13147
- stylesheets: this.config.stylesheets
13168
+ stylesheets: this.config.stylesheets,
13169
+ className: this.config.classNames.sandbox
13148
13170
  });
13149
13171
  this.editableArea = this.sandbox.getIframe();
13150
13172
 
@@ -13178,7 +13200,7 @@ wysihtml5.views.View = Base.extend(
13178
13200
  }
13179
13201
 
13180
13202
  // Make sure our selection handler is ready
13181
- this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.uneditableContainerClassname);
13203
+ this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.classNames.uneditableContainer);
13182
13204
 
13183
13205
  // Make sure commands dispatcher is ready
13184
13206
  this.commands = new wysihtml5.Commands(this.parent);
@@ -13189,7 +13211,7 @@ wysihtml5.views.View = Base.extend(
13189
13211
  ]).from(this.textarea.element).to(this.element);
13190
13212
  }
13191
13213
 
13192
- dom.addClass(this.element, this.config.composerClassName);
13214
+ dom.addClass(this.element, this.config.classNames.composer);
13193
13215
  //
13194
13216
  // Make the editor look like the original textarea, by syncing styles
13195
13217
  if (this.config.style && !this.config.contentEditableMode) {
@@ -13215,7 +13237,7 @@ wysihtml5.views.View = Base.extend(
13215
13237
  ? this.config.placeholder
13216
13238
  : ((this.config.noTextarea) ? this.editableArea.getAttribute("data-placeholder") : this.textarea.element.getAttribute("placeholder"));
13217
13239
  if (placeholderText) {
13218
- dom.simulatePlaceholder(this.parent, this, placeholderText);
13240
+ dom.simulatePlaceholder(this.parent, this, placeholderText, this.config.classNames.placeholder);
13219
13241
  }
13220
13242
 
13221
13243
  // Make sure that the browser avoids using inline styles whenever possible
@@ -13267,7 +13289,7 @@ wysihtml5.views.View = Base.extend(
13267
13289
  this.parent.on("newword:composer", function() {
13268
13290
  if (dom.getTextContent(that.element).match(dom.autoLink.URL_REG_EXP)) {
13269
13291
  var nodeWithSelection = that.selection.getSelectedNode(),
13270
- uneditables = that.element.querySelectorAll("." + that.config.uneditableContainerClassname),
13292
+ uneditables = that.element.querySelectorAll("." + that.config.classNames.uneditableContainer),
13271
13293
  isInUneditable = false;
13272
13294
 
13273
13295
  for (var i = uneditables.length; i--;) {
@@ -13276,12 +13298,12 @@ wysihtml5.views.View = Base.extend(
13276
13298
  }
13277
13299
  }
13278
13300
 
13279
- if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.uneditableContainerClassname]);
13301
+ if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.classNames.uneditableContainer]);
13280
13302
  }
13281
13303
  });
13282
13304
 
13283
13305
  dom.observe(this.element, "blur", function() {
13284
- dom.autoLink(that.element, [that.config.uneditableContainerClassname]);
13306
+ dom.autoLink(that.element, [that.config.classNames.uneditableContainer]);
13285
13307
  });
13286
13308
  }
13287
13309
 
@@ -13716,7 +13738,7 @@ wysihtml5.views.View = Base.extend(
13716
13738
  // If found an uneditable before caret then notify it before deletion
13717
13739
  var handleUneditableDeletion = function(composer) {
13718
13740
  var before = composer.selection.getBeforeSelection(true);
13719
- if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.uneditableContainerClassname)) {
13741
+ if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.classNames.uneditableContainer)) {
13720
13742
  if (fixLastBrDeletionInTable(composer, true)) {
13721
13743
  return true;
13722
13744
  }
@@ -13881,7 +13903,7 @@ wysihtml5.views.View = Base.extend(
13881
13903
  // Make sure that images are selected when clicking on them
13882
13904
  var target = event.target,
13883
13905
  allImages = this.element.querySelectorAll('img'),
13884
- notMyImages = this.element.querySelectorAll('.' + this.config.uneditableContainerClassname + ' img'),
13906
+ notMyImages = this.element.querySelectorAll('.' + this.config.classNames.uneditableContainer + ' img'),
13885
13907
  myImages = wysihtml5.lang.array(allImages).without(notMyImages);
13886
13908
 
13887
13909
  if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
@@ -13911,10 +13933,10 @@ wysihtml5.views.View = Base.extend(
13911
13933
  };
13912
13934
 
13913
13935
  var handleClick = function(event) {
13914
- if (this.config.uneditableContainerClassname) {
13936
+ if (this.config.classNames.uneditableContainer) {
13915
13937
  // If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
13916
13938
  // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
13917
- var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.uneditableContainerClassname }, false, this.element);
13939
+ var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.classNames.uneditableContainer }, false, this.element);
13918
13940
  if (uneditable) {
13919
13941
  this.selection.setAfter(uneditable);
13920
13942
  }
@@ -14338,10 +14360,6 @@ wysihtml5.views.View = Base.extend(
14338
14360
  pasteParserRulesets: null,
14339
14361
  // Parser method to use when the user inserts content
14340
14362
  parser: wysihtml5.dom.parse,
14341
- // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
14342
- composerClassName: "wysihtml5-editor",
14343
- // Class name to add to the body when the wysihtml5 editor is supported
14344
- bodyClassName: "wysihtml5-supported",
14345
14363
  // By default wysihtml5 will insert a <br> for line breaks, set this to false to use <p>
14346
14364
  useLineBreaks: true,
14347
14365
  // Array (or single string) of stylesheet urls to be loaded in the editor's iframe
@@ -14354,9 +14372,18 @@ wysihtml5.views.View = Base.extend(
14354
14372
  cleanUp: true,
14355
14373
  // Whether to use div instead of secure iframe
14356
14374
  contentEditableMode: false,
14357
- // Classname of container that editor should not touch and pass through
14358
- // Pass false to disable
14359
- uneditableContainerClassname: "wysihtml5-uneditable-container",
14375
+ classNames: {
14376
+ // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
14377
+ composer: "wysihtml5-editor",
14378
+ // Class name to add to the body when the wysihtml5 editor is supported
14379
+ body: "wysihtml5-supported",
14380
+ // classname added to editable area element (iframe/div) on creation
14381
+ sandbox: "wysihtml5-sandbox",
14382
+ // class on editable area with placeholder
14383
+ placeholder: "wysihtml5-placeholder",
14384
+ // Classname of container that editor should not touch and pass through
14385
+ uneditableContainer: "wysihtml5-uneditable-container"
14386
+ },
14360
14387
  // Browsers that support copied source handling will get a marking of the origin of the copied source (for determinig code cleanup rules on paste)
14361
14388
  // Also copied source is based directly on selection -
14362
14389
  // (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).
@@ -14368,9 +14395,14 @@ wysihtml5.views.View = Base.extend(
14368
14395
  /** @scope wysihtml5.Editor.prototype */ {
14369
14396
  constructor: function(editableElement, config) {
14370
14397
  this.editableElement = typeof(editableElement) === "string" ? document.getElementById(editableElement) : editableElement;
14371
- this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
14398
+ this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config, true).get();
14372
14399
  this._isCompatible = wysihtml5.browser.supported();
14373
14400
 
14401
+ // make sure that rules override instead of extend
14402
+ if (config && config.parserRules) {
14403
+ this.config.parserRules = wysihtml5.lang.object(config.parserRules).clone(true);
14404
+ }
14405
+
14374
14406
  if (this.editableElement.nodeName.toLowerCase() != "textarea") {
14375
14407
  this.config.contentEditableMode = true;
14376
14408
  this.config.noTextarea = true;
@@ -14388,7 +14420,7 @@ wysihtml5.views.View = Base.extend(
14388
14420
  }
14389
14421
 
14390
14422
  // Add class name to body, to indicate that the editor is supported
14391
- wysihtml5.dom.addClass(document.body, this.config.bodyClassName);
14423
+ wysihtml5.dom.addClass(document.body, this.config.classNames.body);
14392
14424
 
14393
14425
  this.composer = new wysihtml5.views.Composer(this, this.editableElement, this.config);
14394
14426
  this.currentView = this.composer;
@@ -14474,7 +14506,7 @@ wysihtml5.views.View = Base.extend(
14474
14506
  "rules": this.config.parserRules,
14475
14507
  "cleanUp": this.config.cleanUp,
14476
14508
  "context": parseContext,
14477
- "uneditableClass": this.config.uneditableContainerClassname,
14509
+ "uneditableClass": this.config.classNames.uneditableContainer,
14478
14510
  "clearInternals" : clearInternals
14479
14511
  });
14480
14512
  if (typeof(htmlOrElement) === "object") {
@@ -14520,7 +14552,7 @@ wysihtml5.views.View = Base.extend(
14520
14552
  var cleanHtml = wysihtml5.quirks.cleanPastedHTML(oldHtml, {
14521
14553
  "referenceNode": this.composer.element,
14522
14554
  "rules": this.config.pasteParserRulesets || [{"set": this.config.parserRules}],
14523
- "uneditableClass": this.config.uneditableContainerClassname
14555
+ "uneditableClass": this.config.classNames.uneditableContainer
14524
14556
  });
14525
14557
  this.composer.selection.deleteContents();
14526
14558
  this.composer.selection.insertHTML(cleanHtml);
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license wysihtml5x v0.5.0-beta6
3
- * https://github.com/Edicy/wysihtml5
2
+ * @license wysihtml v0.5.0-beta7
3
+ * https://github.com/Voog/wysihtml
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
6
6
  * Secondary author of extended features: Oliver Pulges (https://github.com/pulges)
@@ -10,7 +10,7 @@
10
10
  *
11
11
  */
12
12
  var wysihtml5 = {
13
- version: "0.5.0-beta6",
13
+ version: "0.5.0-beta7",
14
14
 
15
15
  // namespaces
16
16
  commands: {},
@@ -5081,9 +5081,17 @@ wysihtml5.browser = (function() {
5081
5081
  * wysihtml5.lang.object({ foo: 1, bar: 1 }).merge({ bar: 2, baz: 3 }).get();
5082
5082
  * // => { foo: 1, bar: 2, baz: 3 }
5083
5083
  */
5084
- merge: function(otherObj) {
5084
+ merge: function(otherObj, deep) {
5085
5085
  for (var i in otherObj) {
5086
- obj[i] = otherObj[i];
5086
+ if (deep && wysihtml5.lang.object(otherObj[i]).isPlainObject() && (typeof obj[i] === "undefined" || wysihtml5.lang.object(obj[i]).isPlainObject())) {
5087
+ if (typeof obj[i] === "undefined") {
5088
+ obj[i] = wysihtml5.lang.object(otherObj[i]).clone(true);
5089
+ } else {
5090
+ wysihtml5.lang.object(obj[i]).merge(wysihtml5.lang.object(otherObj[i]).clone(true));
5091
+ }
5092
+ } else {
5093
+ obj[i] = wysihtml5.lang.object(otherObj[i]).isPlainObject() ? wysihtml5.lang.object(otherObj[i]).clone(true) : otherObj[i];
5094
+ }
5087
5095
  }
5088
5096
  return this;
5089
5097
  },
@@ -5138,7 +5146,7 @@ wysihtml5.browser = (function() {
5138
5146
  },
5139
5147
 
5140
5148
  isPlainObject: function () {
5141
- return Object.prototype.toString.call(obj) === '[object Object]';
5149
+ return obj && Object.prototype.toString.call(obj) === '[object Object]';
5142
5150
  }
5143
5151
  };
5144
5152
  };
@@ -6696,11 +6704,9 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6696
6704
  newAttributeValue;
6697
6705
 
6698
6706
  if (method) {
6699
- if (attributeValue || (attributeName === "alt" && nodeName == "IMG")) {
6700
- newAttributeValue = method(attributeValue);
6701
- if (typeof(newAttributeValue) === "string") {
6702
- return newAttributeValue;
6703
- }
6707
+ newAttributeValue = method(attributeValue, nodeName);
6708
+ if (typeof(newAttributeValue) === "string") {
6709
+ return newAttributeValue;
6704
6710
  }
6705
6711
  }
6706
6712
 
@@ -6935,9 +6941,13 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6935
6941
 
6936
6942
  alt: (function() {
6937
6943
  var REG_EXP = /[^ a-z0-9_\-]/gi;
6938
- return function(attributeValue) {
6944
+ return function(attributeValue, nodeName) {
6939
6945
  if (!attributeValue) {
6940
- return "";
6946
+ if (nodeName === "IMG") {
6947
+ return "";
6948
+ } else {
6949
+ return null;
6950
+ }
6941
6951
  }
6942
6952
  return attributeValue.replace(REG_EXP, "");
6943
6953
  };
@@ -6963,6 +6973,9 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6963
6973
 
6964
6974
  any: (function() {
6965
6975
  return function(attributeValue) {
6976
+ if (!attributeValue) {
6977
+ return null;
6978
+ }
6966
6979
  return attributeValue;
6967
6980
  };
6968
6981
  })()
@@ -7320,6 +7333,9 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7320
7333
  constructor: function(readyCallback, config) {
7321
7334
  this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
7322
7335
  this.config = wysihtml5.lang.object({}).merge(config).get();
7336
+ if (!this.config.className) {
7337
+ this.config.className = "wysihtml5-sandbox";
7338
+ }
7323
7339
  this.editableArea = this._createIframe();
7324
7340
  },
7325
7341
 
@@ -7374,7 +7390,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7374
7390
  _createIframe: function() {
7375
7391
  var that = this,
7376
7392
  iframe = doc.createElement("iframe");
7377
- iframe.className = "wysihtml5-sandbox";
7393
+ iframe.className = this.config.className;
7378
7394
  wysihtml5.dom.setAttributes({
7379
7395
  "security": "restricted",
7380
7396
  "allowtransparency": "true",
@@ -7537,6 +7553,9 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7537
7553
  constructor: function(readyCallback, config, contentEditable) {
7538
7554
  this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
7539
7555
  this.config = wysihtml5.lang.object({}).merge(config).get();
7556
+ if (!this.config.className) {
7557
+ this.config.className = "wysihtml5-sandbox";
7558
+ }
7540
7559
  if (contentEditable) {
7541
7560
  this.element = this._bindElement(contentEditable);
7542
7561
  } else {
@@ -7547,7 +7566,7 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7547
7566
  // creates a new contenteditable and initiates it
7548
7567
  _createElement: function() {
7549
7568
  var element = doc.createElement("div");
7550
- element.className = "wysihtml5-sandbox";
7569
+ element.className = this.config.className;
7551
7570
  this._loadElement(element);
7552
7571
  return element;
7553
7572
  },
@@ -7626,8 +7645,8 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
7626
7645
  * wysihtml.dom.simulatePlaceholder(this, composer, "Foobar");
7627
7646
  */
7628
7647
  (function(dom) {
7629
- dom.simulatePlaceholder = function(editor, view, placeholderText) {
7630
- var CLASS_NAME = "placeholder",
7648
+ dom.simulatePlaceholder = function(editor, view, placeholderText, placeholderClassName) {
7649
+ var CLASS_NAME = placeholderClassName || "wysihtml5-placeholder",
7631
7650
  unset = function() {
7632
7651
  var composerIsVisible = view.element.offsetWidth > 0 && view.element.offsetHeight > 0;
7633
7652
  if (view.hasPlaceholderSet()) {
@@ -11355,7 +11374,7 @@ wysihtml5.Commands = Base.extend(
11355
11374
  function cleanup(composer) {
11356
11375
  var container = composer.element,
11357
11376
  allElements = container.querySelectorAll(BLOCK_ELEMENTS),
11358
- uneditables = container.querySelectorAll(composer.config.uneditableContainerClassname),
11377
+ uneditables = container.querySelectorAll(composer.config.classNames.uneditableContainer),
11359
11378
  elements = wysihtml5.lang.array(allElements).without(uneditables);
11360
11379
 
11361
11380
  for (var i = elements.length; i--;) {
@@ -11627,7 +11646,7 @@ wysihtml5.Commands = Base.extend(
11627
11646
  state = this.state(composer, command, options);
11628
11647
  if (state) {
11629
11648
  bookmark = rangy.saveSelection(composer.win);
11630
- for (var j in state) {
11649
+ for (var j = 0, jmax = state.length; j < jmax; j++) {
11631
11650
  removeOptionsFromElement(state[j], options, composer);
11632
11651
  }
11633
11652
  }
@@ -11650,7 +11669,7 @@ wysihtml5.Commands = Base.extend(
11650
11669
  composer.selection.selectLine();
11651
11670
  }
11652
11671
  }
11653
-
11672
+
11654
11673
  // And get all selection ranges of current composer and iterat
11655
11674
  ranges = composer.selection.getOwnRanges();
11656
11675
  for (var i = ranges.length; i--;) {
@@ -12212,7 +12231,7 @@ wysihtml5.Commands = Base.extend(
12212
12231
 
12213
12232
  if (tempElement) {
12214
12233
  isEmpty = wysihtml5.lang.array(["", "<br>", wysihtml5.INVISIBLE_SPACE]).contains(tempElement.innerHTML);
12215
- list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.uneditableContainerClassname);
12234
+ list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.classNames.uneditableContainer);
12216
12235
  if (isEmpty) {
12217
12236
  composer.selection.selectNode(list.querySelector("li"), true);
12218
12237
  }
@@ -12441,7 +12460,7 @@ wysihtml5.Commands = Base.extend(
12441
12460
  for (row = 0; row < value.rows; row ++) {
12442
12461
  html += '<tr>';
12443
12462
  for (col = 0; col < value.cols; col ++) {
12444
- html += "<td></td>";
12463
+ html += "<td><br></td>";
12445
12464
  }
12446
12465
  html += '</tr>';
12447
12466
  }
@@ -13123,14 +13142,17 @@ wysihtml5.views.View = Base.extend(
13123
13142
 
13124
13143
  _initContentEditableArea: function() {
13125
13144
  var that = this;
13126
-
13127
13145
  if (this.config.noTextarea) {
13128
13146
  this.sandbox = new dom.ContentEditableArea(function() {
13129
13147
  that._create();
13130
- }, {}, this.editableArea);
13148
+ }, {
13149
+ className: this.config.classNames.sandbox
13150
+ }, this.editableArea);
13131
13151
  } else {
13132
13152
  this.sandbox = new dom.ContentEditableArea(function() {
13133
13153
  that._create();
13154
+ }, {
13155
+ className: this.config.classNames.sandbox
13134
13156
  });
13135
13157
  this.editableArea = this.sandbox.getContentEditable();
13136
13158
  dom.insert(this.editableArea).after(this.textarea.element);
@@ -13140,11 +13162,11 @@ wysihtml5.views.View = Base.extend(
13140
13162
 
13141
13163
  _initSandbox: function() {
13142
13164
  var that = this;
13143
-
13144
13165
  this.sandbox = new dom.Sandbox(function() {
13145
13166
  that._create();
13146
13167
  }, {
13147
- stylesheets: this.config.stylesheets
13168
+ stylesheets: this.config.stylesheets,
13169
+ className: this.config.classNames.sandbox
13148
13170
  });
13149
13171
  this.editableArea = this.sandbox.getIframe();
13150
13172
 
@@ -13178,7 +13200,7 @@ wysihtml5.views.View = Base.extend(
13178
13200
  }
13179
13201
 
13180
13202
  // Make sure our selection handler is ready
13181
- this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.uneditableContainerClassname);
13203
+ this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.classNames.uneditableContainer);
13182
13204
 
13183
13205
  // Make sure commands dispatcher is ready
13184
13206
  this.commands = new wysihtml5.Commands(this.parent);
@@ -13189,7 +13211,7 @@ wysihtml5.views.View = Base.extend(
13189
13211
  ]).from(this.textarea.element).to(this.element);
13190
13212
  }
13191
13213
 
13192
- dom.addClass(this.element, this.config.composerClassName);
13214
+ dom.addClass(this.element, this.config.classNames.composer);
13193
13215
  //
13194
13216
  // Make the editor look like the original textarea, by syncing styles
13195
13217
  if (this.config.style && !this.config.contentEditableMode) {
@@ -13215,7 +13237,7 @@ wysihtml5.views.View = Base.extend(
13215
13237
  ? this.config.placeholder
13216
13238
  : ((this.config.noTextarea) ? this.editableArea.getAttribute("data-placeholder") : this.textarea.element.getAttribute("placeholder"));
13217
13239
  if (placeholderText) {
13218
- dom.simulatePlaceholder(this.parent, this, placeholderText);
13240
+ dom.simulatePlaceholder(this.parent, this, placeholderText, this.config.classNames.placeholder);
13219
13241
  }
13220
13242
 
13221
13243
  // Make sure that the browser avoids using inline styles whenever possible
@@ -13267,7 +13289,7 @@ wysihtml5.views.View = Base.extend(
13267
13289
  this.parent.on("newword:composer", function() {
13268
13290
  if (dom.getTextContent(that.element).match(dom.autoLink.URL_REG_EXP)) {
13269
13291
  var nodeWithSelection = that.selection.getSelectedNode(),
13270
- uneditables = that.element.querySelectorAll("." + that.config.uneditableContainerClassname),
13292
+ uneditables = that.element.querySelectorAll("." + that.config.classNames.uneditableContainer),
13271
13293
  isInUneditable = false;
13272
13294
 
13273
13295
  for (var i = uneditables.length; i--;) {
@@ -13276,12 +13298,12 @@ wysihtml5.views.View = Base.extend(
13276
13298
  }
13277
13299
  }
13278
13300
 
13279
- if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.uneditableContainerClassname]);
13301
+ if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.classNames.uneditableContainer]);
13280
13302
  }
13281
13303
  });
13282
13304
 
13283
13305
  dom.observe(this.element, "blur", function() {
13284
- dom.autoLink(that.element, [that.config.uneditableContainerClassname]);
13306
+ dom.autoLink(that.element, [that.config.classNames.uneditableContainer]);
13285
13307
  });
13286
13308
  }
13287
13309
 
@@ -13716,7 +13738,7 @@ wysihtml5.views.View = Base.extend(
13716
13738
  // If found an uneditable before caret then notify it before deletion
13717
13739
  var handleUneditableDeletion = function(composer) {
13718
13740
  var before = composer.selection.getBeforeSelection(true);
13719
- if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.uneditableContainerClassname)) {
13741
+ if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.classNames.uneditableContainer)) {
13720
13742
  if (fixLastBrDeletionInTable(composer, true)) {
13721
13743
  return true;
13722
13744
  }
@@ -13881,7 +13903,7 @@ wysihtml5.views.View = Base.extend(
13881
13903
  // Make sure that images are selected when clicking on them
13882
13904
  var target = event.target,
13883
13905
  allImages = this.element.querySelectorAll('img'),
13884
- notMyImages = this.element.querySelectorAll('.' + this.config.uneditableContainerClassname + ' img'),
13906
+ notMyImages = this.element.querySelectorAll('.' + this.config.classNames.uneditableContainer + ' img'),
13885
13907
  myImages = wysihtml5.lang.array(allImages).without(notMyImages);
13886
13908
 
13887
13909
  if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
@@ -13911,10 +13933,10 @@ wysihtml5.views.View = Base.extend(
13911
13933
  };
13912
13934
 
13913
13935
  var handleClick = function(event) {
13914
- if (this.config.uneditableContainerClassname) {
13936
+ if (this.config.classNames.uneditableContainer) {
13915
13937
  // If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
13916
13938
  // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
13917
- var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.uneditableContainerClassname }, false, this.element);
13939
+ var uneditable = wysihtml5.dom.getParentElement(event.target, { query: "." + this.config.classNames.uneditableContainer }, false, this.element);
13918
13940
  if (uneditable) {
13919
13941
  this.selection.setAfter(uneditable);
13920
13942
  }
@@ -14338,10 +14360,6 @@ wysihtml5.views.View = Base.extend(
14338
14360
  pasteParserRulesets: null,
14339
14361
  // Parser method to use when the user inserts content
14340
14362
  parser: wysihtml5.dom.parse,
14341
- // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
14342
- composerClassName: "wysihtml5-editor",
14343
- // Class name to add to the body when the wysihtml5 editor is supported
14344
- bodyClassName: "wysihtml5-supported",
14345
14363
  // By default wysihtml5 will insert a <br> for line breaks, set this to false to use <p>
14346
14364
  useLineBreaks: true,
14347
14365
  // Array (or single string) of stylesheet urls to be loaded in the editor's iframe
@@ -14354,9 +14372,18 @@ wysihtml5.views.View = Base.extend(
14354
14372
  cleanUp: true,
14355
14373
  // Whether to use div instead of secure iframe
14356
14374
  contentEditableMode: false,
14357
- // Classname of container that editor should not touch and pass through
14358
- // Pass false to disable
14359
- uneditableContainerClassname: "wysihtml5-uneditable-container",
14375
+ classNames: {
14376
+ // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
14377
+ composer: "wysihtml5-editor",
14378
+ // Class name to add to the body when the wysihtml5 editor is supported
14379
+ body: "wysihtml5-supported",
14380
+ // classname added to editable area element (iframe/div) on creation
14381
+ sandbox: "wysihtml5-sandbox",
14382
+ // class on editable area with placeholder
14383
+ placeholder: "wysihtml5-placeholder",
14384
+ // Classname of container that editor should not touch and pass through
14385
+ uneditableContainer: "wysihtml5-uneditable-container"
14386
+ },
14360
14387
  // Browsers that support copied source handling will get a marking of the origin of the copied source (for determinig code cleanup rules on paste)
14361
14388
  // Also copied source is based directly on selection -
14362
14389
  // (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).
@@ -14368,9 +14395,14 @@ wysihtml5.views.View = Base.extend(
14368
14395
  /** @scope wysihtml5.Editor.prototype */ {
14369
14396
  constructor: function(editableElement, config) {
14370
14397
  this.editableElement = typeof(editableElement) === "string" ? document.getElementById(editableElement) : editableElement;
14371
- this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
14398
+ this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config, true).get();
14372
14399
  this._isCompatible = wysihtml5.browser.supported();
14373
14400
 
14401
+ // make sure that rules override instead of extend
14402
+ if (config && config.parserRules) {
14403
+ this.config.parserRules = wysihtml5.lang.object(config.parserRules).clone(true);
14404
+ }
14405
+
14374
14406
  if (this.editableElement.nodeName.toLowerCase() != "textarea") {
14375
14407
  this.config.contentEditableMode = true;
14376
14408
  this.config.noTextarea = true;
@@ -14388,7 +14420,7 @@ wysihtml5.views.View = Base.extend(
14388
14420
  }
14389
14421
 
14390
14422
  // Add class name to body, to indicate that the editor is supported
14391
- wysihtml5.dom.addClass(document.body, this.config.bodyClassName);
14423
+ wysihtml5.dom.addClass(document.body, this.config.classNames.body);
14392
14424
 
14393
14425
  this.composer = new wysihtml5.views.Composer(this, this.editableElement, this.config);
14394
14426
  this.currentView = this.composer;
@@ -14474,7 +14506,7 @@ wysihtml5.views.View = Base.extend(
14474
14506
  "rules": this.config.parserRules,
14475
14507
  "cleanUp": this.config.cleanUp,
14476
14508
  "context": parseContext,
14477
- "uneditableClass": this.config.uneditableContainerClassname,
14509
+ "uneditableClass": this.config.classNames.uneditableContainer,
14478
14510
  "clearInternals" : clearInternals
14479
14511
  });
14480
14512
  if (typeof(htmlOrElement) === "object") {
@@ -14520,7 +14552,7 @@ wysihtml5.views.View = Base.extend(
14520
14552
  var cleanHtml = wysihtml5.quirks.cleanPastedHTML(oldHtml, {
14521
14553
  "referenceNode": this.composer.element,
14522
14554
  "rules": this.config.pasteParserRulesets || [{"set": this.config.parserRules}],
14523
- "uneditableClass": this.config.uneditableContainerClassname
14555
+ "uneditableClass": this.config.classNames.uneditableContainer
14524
14556
  });
14525
14557
  this.composer.selection.deleteContents();
14526
14558
  this.composer.selection.insertHTML(cleanHtml);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wysihtml-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.beta6
4
+ version: 0.5.0.beta7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanel Jakobsoo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-03 00:00:00.000000000 Z
11
+ date: 2015-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties