Socket
Socket
Sign inDemoInstall

medium-editor

Package Overview
Dependencies
Maintainers
6
Versions
125
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

medium-editor - npm Package Compare versions

Comparing version 5.16.1 to 5.17.0

.github/ISSUE_TEMPLATE.md

69

API.md

@@ -11,2 +11,4 @@ # MediumEditor Object API (v5.0.0)

- [`setup()`](#setup)
- [`addElements()`](#addelementselements)
- [`removeElements()`](#removeelementselements)
- [Event Functions](#event-functions)

@@ -37,2 +39,3 @@ - [`on(targets, event, listener, useCapture)`](#ontargets-event-listener-usecapture)

- [Helper Functions](#helper-functions)
- [`checkContentChanged(editable)`](#checkContentChangededitable)
- [`delay(fn)`](#delayfn)

@@ -51,3 +54,6 @@ - [`getExtensionByName(name)`](#getextensionbynamename)

* Convert all passed in elements into `contenteditable` elements.
* For any `<textarea>` elements, hide the `<textarea>`, create a new `<div contenteditable=true>` element, and ensure the 2 elements remain sync'd.
* For any `<textarea>` elements:
* Hide the `<textarea>`
* Create a new `<div contenteditable=true>` element and add it to the elements array.
* Ensure the 2 elements remain sync'd.
* Initialize any custom extensions or buttons passed in.

@@ -59,3 +65,3 @@ * Create any additional elements needed.

_**elements** (`String` | `HTMLElement` | `Array`)_:
_**elements** (`String` | `HTMLElement` | `Array` | `NodeList` | `HTMLCollection`)_:

@@ -88,2 +94,51 @@ 1. `String`: If passed as a string, this is used as a selector in a call to `document.querySelectorAll()` to find elements on the page. All results are stored in the internal list of **elements**.

***
### `addElements(elements)`
Dynamically add one or more elements to an already initialized instance of MediumEditor.
Passing an elements or array of elements to `addElements(elements)` will:
* Add the given element or array of elements to the editor **elements**
* Ensure the element(s) are initialized with the proper attributes and event handlers as if the element had been passed during instantiation of the editor
* For any `<textarea>` elements:
* Hide the `<textarea>`
* Create a new `<div contenteditable=true>` element and add it to the editor **elements**
* Ensure the 2 elements remain sync'd.
* Be intelligent enough to run the necessary code only once per element, no matter how often you will call it
So, every element you pass to `addElements` will turn into a fully supported contenteditable too - even earlier calls to `editor.subscribe(..)`
for custom events will work on the newly added element(s).
**Arguments**
_**elements** (`String` | `HTMLElement` | `Array` | `NodeList` | `HTMLCollection`)_:
1. `String`: If passed as a string, this is used as a selector in a call to `document.querySelectorAll()` to find elements on the page.
2. `HTMLElement`: If passed as a single element, this will be the only element added to the editor **elements**.
3. `Array` | `NodeList` | `HTMLCollection`: If passed as an `Array`-like collection of `HTMLElement`s, all of these elements will be added to the editor **elements**.
***
### `removeElements(elements)`
Remove one or more elements from an already initialized instance of MediumEditor.
Passing an elements or array of elements to `removeElements(elements)` will:
* Remove the given element or array of elements from the internal `this.elements` array.
* Remove any added event handlers or attributes (with the exception of `contenteditable`).
* Unhide any `<textarea>` elements and remove any created `<div>` elements created for `<textarea>` elements.
Each element itself will remain a contenteditable - it will just remove all event handlers and all references to it so you can safely remove it from DOM.
**Arguments**
_**elements** (`String` | `HTMLElement` | `Array` | `NodeList` | `HTMLCollection`)_:
1. `String`: If passed as a string, this is used as a selector in a call to `document.querySelectorAll()` to find elements on the page.
2. `HTMLElement`: If passed as a single element, this will be the only element removed from the editor **elements**.
3. `Array` | `NodeList` | `HTMLCollection`: If passed as an `Array`-like collection of `HTMLElement`s, all of these elements will be removed from the edtior **elements**.
***
## Event Functions

@@ -296,2 +351,12 @@

### `checkContentChanged(editable)`
Trigger the editor to check for updates to the html, and trigger the `editableInput` event if needed.
**Arguments**
1. _**editable** (`HTMLElement`)_: _**OPTIONAL**_
* The `<div contenteditable=true></div>` element that contains the html that may have changed.
* If no element is provided, the editor will check the currently 'active' editor element (the element with focus).
### `delay(fn)`

@@ -298,0 +363,0 @@

@@ -0,4 +1,15 @@

5.17.0 / 2016-05-17
==================
* Improved paste handling
* Includes proper support for keyboard paste in firefox
* More cleanup when pasting from Word
* Introduce support for adding and removing elements from an existing editor instances
* New addElements and removeElements methods
* Add checkContentChanged method for manually triggering editableInput
* Add selection.selectRange helper method
5.16.1 / 2016-04-14
==================
* Fix incorrect word breaking
* Fix incorrect word breaking

@@ -5,0 +16,0 @@

2

OPTIONS.md

@@ -193,3 +193,3 @@ # MediumEditor Options (v5.0.0)

Options for the toolbar are passed as an object taht is a member of the outer options object. Example:
Options for the toolbar are passed as an object that is a member of the outer options object. Example:
```js

@@ -196,0 +196,0 @@ var editor = new MediumEditor('.editable', {

{
"name": "medium-editor",
"version": "5.16.1",
"version": "5.17.0",
"author": "Davi Ferreira <hi@daviferreira.com>",

@@ -5,0 +5,0 @@ "contributors": [

@@ -468,3 +468,2 @@ # MediumEditor

## Buttons

@@ -528,2 +527,4 @@

* __.setup()__: rebuilds the editor if it has already been destroyed, recreating DOM elements and attaching event handlers
* __.addElements()__: add elements to an already initialized instance of MediumEditor
* __.removeElements()__: remove elements from an already initialized instance of MediumEditor

@@ -563,2 +564,47 @@ ### Event Methods

## Dynamically add/remove elements to your instance
It is possible to dynamically add new elements to your existing MediumtEditor instance:
```javascript
var editor = new MediumEditor('.editable');
editor.subscribe('editableInput', this._handleEditableInput.bind(this));
// imagine an ajax fetch/any other dynamic functionality which will add new '.editable' elements to dom
editor.addElements('.editable');
// OR editor.addElements(document.getElementsByClassName('editable'));
// OR editor.addElements(document.querySelectorAll('.editable'));
```
Passing an elements or array of elements to `addElements(elements)` will:
* Add the given element or array of elements to the internal `this.elements` array.
* Ensure the element(s) are initialized with the proper attributes and event handlers as if the element had been passed during instantiation of the editor.
* For any `<textarea>` elements:
* Hide the `<textarea>`
* Create a new `<div contenteditable=true>` element and add it to the elements array.
* Ensure the 2 elements remain sync'd.
* Be intelligent enough to run the necessary code only once per element, no matter how often you will call it.
### Removing elements dynamically
Straight forward, just call `removeElements` with the elemnt or array of elements you to want to tear down. Each element itself will remain a contenteditable - it will just remove all event handlers and all references to it so you can safely remove it from DOM.
```javascript
editor.removeElements(document.querySelector('#myElement'));
// OR editor.removeElements(document.getElementById('myElement'));
// OR editor.removeElements('#myElement');
// in case you have jQuery and don't exactly know when an element was removed, for example after routing state change
var removedElements = [];
editor.elements.forEach(function (element) {
// check if the element is still available in current DOM
if (!$(element).parents('body').length) {
removedElements.push(element);
}
});
editor.removeElements(removedElements);
```
## Capturing DOM changes

@@ -565,0 +611,0 @@

@@ -1,2 +0,3 @@

/*global fireEvent, selectElementContents */
/*global fireEvent, selectElementContents,
selectElementContentsAndFire */

@@ -104,2 +105,47 @@ describe('Core-API', function () {

});
describe('checkContentChanged', function () {
it('should trigger editableInput when called after the html has changed', function () {
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editableInput', spy);
expect(spy).not.toHaveBeenCalled();
selectElementContentsAndFire(this.el.firstChild);
jasmine.clock().tick(1);
this.el.innerHTML = 'lorem ipsum';
expect(spy).not.toHaveBeenCalled();
var obj = { target: this.el, currentTarget: this.el };
editor.checkContentChanged();
expect(spy).toHaveBeenCalledWith(obj, this.el);
});
it('should not trigger editableInput when called after the html has not changed', function () {
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editableInput', spy);
expect(spy).not.toHaveBeenCalled();
selectElementContentsAndFire(this.el.firstChild);
jasmine.clock().tick(1);
this.el.innerHTML = 'lore ipsum';
expect(spy).not.toHaveBeenCalled();
editor.checkContentChanged();
expect(spy).not.toHaveBeenCalled();
});
});
});

@@ -87,2 +87,5 @@ /*global selectElementContentsAndFire */

return document.execCommand.apply(document, arguments);
},
getElementById: function () {
return document.getElementById.apply(document, arguments);
}

@@ -89,0 +92,0 @@ },

@@ -241,1 +241,1036 @@ /*global atob, unescape, Uint8Array, Blob*/

}
var WORD_PASTE_EXAMPLE = ['<html xmlns:o="urn:schemas-microsoft-com:office:office"',
'xmlns:w="urn:schemas-microsoft-com:office:word"',
'xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"',
'xmlns="http://www.w3.org/TR/REC-html40">',
'',
'<head>',
'<meta name=Title content="">',
'<meta name=Keywords content="">',
'<meta http-equiv=Content-Type content="text/html; charset=utf-8">',
'<meta name=ProgId content=Word.Document>',
'<meta name=Generator content="Microsoft Word 15">',
'<meta name=Originator content="Microsoft Word 15">',
'<link rel=File-List',
'href="file://localhost/Users/nate/Library/Group%20Containers/UBF8T346G9.Office/msoclip1/01/clip_filelist.xml">',
'<!--[if gte mso 9]><xml>',
' <o:OfficeDocumentSettings>',
' <o:AllowPNG/>',
' </o:OfficeDocumentSettings>',
'</xml><![endif]-->',
'<link rel=themeData',
'href="file://localhost/Users/nate/Library/Group%20Containers/UBF8T346G9.Office/msoclip1/01/clip_themedata.thmx">',
'<!--[if gte mso 9]><xml>',
' <w:WordDocument>',
' <w:View>Normal</w:View>',
' <w:Zoom>0</w:Zoom>',
' <w:TrackMoves/>',
' <w:TrackFormatting/>',
' <w:PunctuationKerning/>',
' <w:ValidateAgainstSchemas/>',
' <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>',
' <w:IgnoreMixedContent>false</w:IgnoreMixedContent>',
' <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>',
' <w:DoNotPromoteQF/>',
' <w:LidThemeOther>EN-US</w:LidThemeOther>',
' <w:LidThemeAsian>JA</w:LidThemeAsian>',
' <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>',
' <w:Compatibility>',
' <w:BreakWrappedTables/>',
' <w:SnapToGridInCell/>',
' <w:WrapTextWithPunct/>',
' <w:UseAsianBreakRules/>',
' <w:DontGrowAutofit/>',
' <w:SplitPgBreakAndParaMark/>',
' <w:EnableOpenTypeKerning/>',
' <w:DontFlipMirrorIndents/>',
' <w:OverrideTableStyleHps/>',
' <w:UseFELayout/>',
' </w:Compatibility>',
' <m:mathPr>',
' <m:mathFont m:val="Cambria Math"/>',
' <m:brkBin m:val="before"/>',
' <m:brkBinSub m:val="&#45;-"/>',
' <m:smallFrac m:val="off"/>',
' <m:dispDef/>',
' <m:lMargin m:val="0"/>',
' <m:rMargin m:val="0"/>',
' <m:defJc m:val="centerGroup"/>',
' <m:wrapIndent m:val="1440"/>',
' <m:intLim m:val="subSup"/>',
' <m:naryLim m:val="undOvr"/>',
' </m:mathPr></w:WordDocument>',
'</xml><![endif]--><!--[if gte mso 9]><xml>',
' <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false"',
' DefSemiHidden="false" DefQFormat="false" DefPriority="99"',
' LatentStyleCount="380">',
' <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/>',
' <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 2"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 3"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 4"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 5"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 6"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 7"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 8"/>',
' <w:LsdException Locked="false" Priority="9" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="heading 9"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 6"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 7"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 8"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index 9"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 1"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 2"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 3"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 4"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 5"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 6"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 7"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 8"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" Name="toc 9"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Normal Indent"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="footnote text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="annotation text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="header"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="footer"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="index heading"/>',
' <w:LsdException Locked="false" Priority="35" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="caption"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="table of figures"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="envelope address"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="envelope return"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="footnote reference"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="annotation reference"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="line number"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="page number"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="endnote reference"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="endnote text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="table of authorities"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="macro"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="toa heading"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Bullet"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Number"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Bullet 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Bullet 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Bullet 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Bullet 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Number 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Number 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Number 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Number 5"/>',
' <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Closing"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Signature"/>',
' <w:LsdException Locked="false" Priority="1" SemiHidden="true"',
' UnhideWhenUsed="true" Name="Default Paragraph Font"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text Indent"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Continue"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Continue 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Continue 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Continue 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="List Continue 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Message Header"/>',
' <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Salutation"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Date"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text First Indent"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text First Indent 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Heading"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text Indent 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Body Text Indent 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Block Text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Hyperlink"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="FollowedHyperlink"/>',
' <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/>',
' <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Document Map"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Plain Text"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="E-mail Signature"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Top of Form"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Bottom of Form"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Normal (Web)"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Acronym"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Address"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Cite"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Code"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Definition"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Keyboard"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Preformatted"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Sample"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Typewriter"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="HTML Variable"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Normal Table"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="annotation subject"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="No List"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Outline List 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Outline List 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Outline List 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Simple 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Simple 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Simple 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Classic 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Classic 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Classic 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Classic 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Colorful 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Colorful 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Colorful 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Columns 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Columns 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Columns 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Columns 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Columns 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 6"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 7"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Grid 8"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 6"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 7"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table List 8"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table 3D effects 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table 3D effects 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table 3D effects 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Contemporary"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Elegant"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Professional"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Subtle 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Subtle 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Web 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Web 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Web 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Balloon Text"/>',
' <w:LsdException Locked="false" Priority="39" Name="Table Grid"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Table Theme"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 2"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 3"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 4"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 5"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 6"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 7"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 8"/>',
' <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true"',
' Name="Note Level 9"/>',
' <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/>',
' <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/>',
' <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/>',
' <w:LsdException Locked="false" Priority="34" QFormat="true"',
' Name="List Paragraph"/>',
' <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/>',
' <w:LsdException Locked="false" Priority="30" QFormat="true"',
' Name="Intense Quote"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/>',
' <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/>',
' <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/>',
' <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/>',
' <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/>',
' <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/>',
' <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/>',
' <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/>',
' <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/>',
' <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/>',
' <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/>',
' <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/>',
' <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/>',
' <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/>',
' <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/>',
' <w:LsdException Locked="false" Priority="19" QFormat="true"',
' Name="Subtle Emphasis"/>',
' <w:LsdException Locked="false" Priority="21" QFormat="true"',
' Name="Intense Emphasis"/>',
' <w:LsdException Locked="false" Priority="31" QFormat="true"',
' Name="Subtle Reference"/>',
' <w:LsdException Locked="false" Priority="32" QFormat="true"',
' Name="Intense Reference"/>',
' <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/>',
' <w:LsdException Locked="false" Priority="37" SemiHidden="true"',
' UnhideWhenUsed="true" Name="Bibliography"/>',
' <w:LsdException Locked="false" Priority="39" SemiHidden="true"',
' UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/>',
' <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/>',
' <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/>',
' <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/>',
' <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/>',
' <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/>',
' <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/>',
' <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/>',
' <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/>',
' <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 1"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 1"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 1"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 2"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 2"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 2"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 3"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 3"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 3"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 4"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 4"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 4"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 5"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 5"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 5"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="Grid Table 1 Light Accent 6"/>',
' <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/>',
' <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/>',
' <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/>',
' <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="Grid Table 6 Colorful Accent 6"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="Grid Table 7 Colorful Accent 6"/>',
' <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/>',
' <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/>',
' <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 1"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 1"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 1"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 2"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 2"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 2"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 3"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 3"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 3"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 4"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 4"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 4"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 5"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 5"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 5"/>',
' <w:LsdException Locked="false" Priority="46"',
' Name="List Table 1 Light Accent 6"/>',
' <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/>',
' <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/>',
' <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/>',
' <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/>',
' <w:LsdException Locked="false" Priority="51"',
' Name="List Table 6 Colorful Accent 6"/>',
' <w:LsdException Locked="false" Priority="52"',
' Name="List Table 7 Colorful Accent 6"/>',
' </w:LatentStyles>',
'</xml><![endif]-->',
'<style>',
'<!--',
' /* Font Definitions */',
'@font-face',
' {font-family:"Courier New";',
' panose-1:2 7 3 9 2 2 5 2 4 4;',
' mso-font-charset:0;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:-536859905 -1073711037 9 0 511 0;}',
'@font-face',
' {font-family:Wingdings;',
' panose-1:5 0 0 0 0 0 0 0 0 0;',
' mso-font-charset:2;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:0 268435456 0 0 -2147483648 0;}',
'@font-face',
' {font-family:"MS 明朝";',
' mso-font-charset:128;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}',
'@font-face',
' {font-family:"Cambria Math";',
' panose-1:2 4 5 3 5 4 6 3 2 4;',
' mso-font-charset:0;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:-536870145 1107305727 0 0 415 0;}',
'@font-face',
' {font-family:Cambria;',
' panose-1:2 4 5 3 5 4 6 3 2 4;',
' mso-font-charset:0;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:-536870145 1073743103 0 0 415 0;}',
'@font-face',
' {font-family:"Comic Sans MS";',
' panose-1:3 15 7 2 3 3 2 2 2 4;',
' mso-font-charset:0;',
' mso-generic-font-family:auto;',
' mso-font-pitch:variable;',
' mso-font-signature:647 0 0 0 159 0;}',
' /* Style Definitions */',
'p.MsoNormal, li.MsoNormal, div.MsoNormal',
' {mso-style-unhide:no;',
' mso-style-qformat:yes;',
' mso-style-parent:"";',
' margin:0in;',
' margin-bottom:.0001pt;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph',
' {mso-style-priority:34;',
' mso-style-unhide:no;',
' mso-style-qformat:yes;',
' margin-top:0in;',
' margin-right:0in;',
' margin-bottom:0in;',
' margin-left:.5in;',
' margin-bottom:.0001pt;',
' mso-add-space:auto;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'p.MsoListParagraphCxSpFirst, li.MsoListParagraphCxSpFirst, div.MsoListParagraphCxSpFirst',
' {mso-style-priority:34;',
' mso-style-unhide:no;',
' mso-style-qformat:yes;',
' mso-style-type:export-only;',
' margin-top:0in;',
' margin-right:0in;',
' margin-bottom:0in;',
' margin-left:.5in;',
' margin-bottom:.0001pt;',
' mso-add-space:auto;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'p.MsoListParagraphCxSpMiddle, li.MsoListParagraphCxSpMiddle, div.MsoListParagraphCxSpMiddle',
' {mso-style-priority:34;',
' mso-style-unhide:no;',
' mso-style-qformat:yes;',
' mso-style-type:export-only;',
' margin-top:0in;',
' margin-right:0in;',
' margin-bottom:0in;',
' margin-left:.5in;',
' margin-bottom:.0001pt;',
' mso-add-space:auto;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'p.MsoListParagraphCxSpLast, li.MsoListParagraphCxSpLast, div.MsoListParagraphCxSpLast',
' {mso-style-priority:34;',
' mso-style-unhide:no;',
' mso-style-qformat:yes;',
' mso-style-type:export-only;',
' margin-top:0in;',
' margin-right:0in;',
' margin-bottom:0in;',
' margin-left:.5in;',
' margin-bottom:.0001pt;',
' mso-add-space:auto;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'.MsoChpDefault',
' {mso-style-type:export-only;',
' mso-default-props:yes;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-fareast-font-family:"MS 明朝";',
' mso-fareast-theme-font:minor-fareast;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;',
' mso-bidi-font-family:"Times New Roman";',
' mso-bidi-theme-font:minor-bidi;}',
'@page WordSection1',
' {size:8.5in 11.0in;',
' margin:1.0in 1.0in 1.0in 1.0in;',
' mso-header-margin:.5in;',
' mso-footer-margin:.5in;',
' mso-paper-source:0;}',
'div.WordSection1',
' {page:WordSection1;}',
' /* List Definitions */',
'@list l0',
' {mso-list-id:1751544061;',
' mso-list-type:hybrid;',
' mso-list-template-ids:-622833810 67698689 67698691 67698693 67698689 67698691 67698693 67698689 67698691 67698693;}',
'@list l0:level1',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Symbol;}',
'@list l0:level2',
' {mso-level-number-format:bullet;',
' mso-level-text:o;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:"Courier New";',
' mso-bidi-font-family:"Times New Roman";}',
'@list l0:level3',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Wingdings;}',
'@list l0:level4',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Symbol;}',
'@list l0:level5',
' {mso-level-number-format:bullet;',
' mso-level-text:o;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:"Courier New";',
' mso-bidi-font-family:"Times New Roman";}',
'@list l0:level6',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Wingdings;}',
'@list l0:level7',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Symbol;}',
'@list l0:level8',
' {mso-level-number-format:bullet;',
' mso-level-text:o;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:"Courier New";',
' mso-bidi-font-family:"Times New Roman";}',
'@list l0:level9',
' {mso-level-number-format:bullet;',
' mso-level-text:;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;',
' font-family:Wingdings;}',
'@list l1',
' {mso-list-id:1845972260;',
' mso-list-type:hybrid;',
' mso-list-template-ids:1079810306 67698703 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}',
'@list l1:level1',
' {mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level2',
' {mso-level-number-format:alpha-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level3',
' {mso-level-number-format:roman-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:right;',
' text-indent:-9.0pt;}',
'@list l1:level4',
' {mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level5',
' {mso-level-number-format:alpha-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level6',
' {mso-level-number-format:roman-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:right;',
' text-indent:-9.0pt;}',
'@list l1:level7',
' {mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level8',
' {mso-level-number-format:alpha-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:left;',
' text-indent:-.25in;}',
'@list l1:level9',
' {mso-level-number-format:roman-lower;',
' mso-level-tab-stop:none;',
' mso-level-number-position:right;',
' text-indent:-9.0pt;}',
'ol',
' {margin-bottom:0in;}',
'ul',
' {margin-bottom:0in;}',
'-->',
'</style>',
'<!--[if gte mso 10]>',
'<style>',
' /* Style Definitions */',
'table.MsoNormalTable',
' {mso-style-name:"Table Normal";',
' mso-tstyle-rowband-size:0;',
' mso-tstyle-colband-size:0;',
' mso-style-noshow:yes;',
' mso-style-priority:99;',
' mso-style-parent:"";',
' mso-padding-alt:0in 5.4pt 0in 5.4pt;',
' mso-para-margin:0in;',
' mso-para-margin-bottom:.0001pt;',
' mso-pagination:widow-orphan;',
' font-size:12.0pt;',
' font-family:Cambria;',
' mso-ascii-font-family:Cambria;',
' mso-ascii-theme-font:minor-latin;',
' mso-hansi-font-family:Cambria;',
' mso-hansi-theme-font:minor-latin;}',
'</style>',
'<![endif]-->',
'</head>',
'',
'<body bgcolor=white lang=EN-US style=\'tab-interval:.5in\'>',
'<!--StartFragment-->',
'',
'<p class=MsoNormal><span style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>My',
'complicated <b style=\'mso-bidi-font-weight:normal\'>word document renders</b> <i ',
'style=\'mso-bidi-font-style:normal\'>like this in the card</i> generator.<o:p></o:p></span></p>',
'',
'<p class=MsoNormal><span style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'><o:p>&nbsp;</o:p></span></p>',
'',
'<p class=MsoNormal><span style=\'font-size:24.0pt;font-family:"Comic Sans MS"\'>Test',
'big <span style=\'color:red\'>text</span><o:p></o:p></span></p>',
'',
'<p class=MsoNormal><span style=\'font-size:24.0pt;font-family:"Comic Sans MS"\'><o:p>&nbsp;</o:p></span></p>',
'',
'<p class=MsoNormal><span style=\'font-size:24.0pt;font-family:"Comic Sans MS"\'><o:p>&nbsp;</o:p></span></p>',
'',
'<p class=MsoNormal><span style=\'font-size:18.0pt;font-family:"Comic Sans MS"\'>Testing',
'smaller text and <s>crossed out text.<o:p></o:p></s></span></p>',
'',
'<p class=MsoNormal><s><span style=\'font-size:18.0pt;font-family:"Comic Sans MS"\'><o:p><span ',
' style=\'text-decoration:none\'>&nbsp;</span></o:p></span></s></p>',
'',
'<p class=MsoListParagraphCxSpFirst style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:Symbol;mso-fareast-font-family:Symbol;',
'mso-bidi-font-family:Symbol\'><span style=\'mso-list:Ignore\'>·<span ',
'style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>Test list<o:p></o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpMiddle style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:Symbol;mso-fareast-font-family:Symbol;',
'mso-bidi-font-family:Symbol\'><span style=\'mso-list:Ignore\'>·<span ',
'style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>Test<o:p></o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpMiddle style=\'margin-left:1.0in;mso-add-space:',
'auto;text-indent:-.25in;mso-list:l0 level2 lfo1\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:"Courier New";mso-fareast-font-family:"Courier New"\'><span ',
'style=\'mso-list:Ignore\'>o<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;',
'</span></span></span><![endif]><span style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>Test',
'indented<o:p></o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpMiddle style=\'text-indent:-.25in;mso-list:l0 level1 lfo1\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:Symbol;mso-fareast-font-family:Symbol;',
'mso-bidi-font-family:Symbol\'><span style=\'mso-list:Ignore\'>·<span ',
'style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>Tes6t<o:p></o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpMiddle><span style=\'font-size:14.0pt;font-family:',
'"Comic Sans MS"\'><o:p>&nbsp;</o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpMiddle style=\'text-indent:-.25in;mso-list:l1 level1 lfo2\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS";mso-fareast-font-family:',
'"Comic Sans MS";mso-bidi-font-family:"Comic Sans MS"\'><span style=\'mso-list:',
'Ignore\'>1.<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>tes t test test<o:p></o:p></span></p>',
'',
'<p class=MsoListParagraphCxSpLast style=\'margin-left:1.0in;mso-add-space:auto;',
'text-indent:-.25in;mso-list:l1 level2 lfo2\'><![if !supportLists]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS";mso-fareast-font-family:',
'"Comic Sans MS";mso-bidi-font-family:"Comic Sans MS"\'><span style=\'mso-list:',
'Ignore\'>a.<span style=\'font:7.0pt "Times New Roman"\'>&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span ',
'style=\'font-size:14.0pt;font-family:"Comic Sans MS"\'>tes t indented<o:p></o:p></span></p>',
'',
'<p class=MsoNormal><o:p>&nbsp;</o:p></p>',
'',
'<!--EndFragment-->',
'</body>',
'',
'</html>'].join('');

@@ -163,4 +163,4 @@ /*global _ */

firstId = editor1.id,
editor2 = this.newMediumEditor('.editor'),
editor3 = this.newMediumEditor('.editor');
editor2 = this.newMediumEditor(this.createElement('div')),
editor3 = this.newMediumEditor(this.createElement('div'));

@@ -167,0 +167,0 @@ expect(editor2.id).toBe(firstId + 1);

/*global selectElementContents,
selectElementContentsAndFire */
selectElementContentsAndFire,
fireEvent, prepareEvent,
firePreparedEvent, WORD_PASTE_EXAMPLE */

@@ -42,2 +44,7 @@ describe('Pasting content', function () {

output: '<p>One</p><p>Two</p>'
},
{
source: 'Microsoft Word - full document paste',
paste: WORD_PASTE_EXAMPLE,
output: '<p>Mycomplicated <b>word document renders</b> <i>like this in the card</i> generator.</p><p>Testbig text</p><p>Testingsmaller text and <s>crossed out text.</s></p><p>· Test list</p><p>· Test</p><p>o Testindented</p><p>· Tes6t</p><p>1. tes t test test</p><p>a. tes t indented</p>'
}

@@ -121,2 +128,3 @@ ],

clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {

@@ -161,2 +169,3 @@ // do we need to return different results for the different types? text/plain, text/html

clipboardData: {
types: ['text/plain'],
getData: function (clipboardType) {

@@ -221,2 +230,266 @@ if (clipboardType === 'text/plain') {

describe('using keyboard', function () {
it('should insert a custom paste-bin on keydown of CTRL + V', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
});
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var pasteBin = contentEditables[1];
expect(pasteBin.innerHTML).toBe('%ME_PASTEBIN%');
expect(pasteBin.parentNode).toBe(document.body);
var range = document.getSelection().getRangeAt(0);
expect(MediumEditor.util.isDescendant(pasteBin, range.commonAncestorContainer, true)).toBe(true, 'Select is not within the paste bin');
expect(range.toString()).toBe('%ME_PASTEBIN%');
});
it('should trigger handlePasteBinPaste when pasting into paste-bin', function () {
spyOn(MediumEditor.extensions.paste.prototype, 'handlePasteBinPaste').and.callThrough();
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteHandler = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var pasteBin = contentEditables[1],
evt = prepareEvent(pasteBin, 'paste');
firePreparedEvent(evt, pasteBin, 'paste');
expect(pasteHandler.handlePasteBinPaste).toHaveBeenCalledWith(evt);
});
it('should do nothing if default was prevented on paste event of the paste-bin', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
defaultPrevented: true,
preventDefault: function () {},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'pasted content';
}
}
};
spyOn(evt, 'preventDefault');
// Paste should insert data from the clipboard, and prevent paste from happening in the paste-bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
expect(this.el.innerHTML).toBe('hhh');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use clipboard data if available and not allow paste to happen', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'pasted content';
}
}
};
spyOn(evt, 'preventDefault');
// Paste should insert data from the clipboard, and prevent paste from happening in the paste-bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).toHaveBeenCalled();
expect(this.el.innerHTML).toBe('pasted content');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use html from the paste bin when clipboard data is not available', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return null;
}
}
},
testHTML = '<b>HTML from <u>paste bin</u></b>',
pasteBin = contentEditables[1];
pasteBin.innerHTML = testHTML;
spyOn(evt, 'preventDefault');
// Paste should not be prevented on the paste bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
jasmine.clock().tick(1);
// HTML from paste-bin should now be in the editor
expect(this.el.innerHTML).toMatch(new RegExp(testHTML + '(<br/?>)?'));
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
it('should use plain text when clipboard data is not available and paste-bin does not have real content', function () {
var editor = this.newMediumEditor('.editor', {
paste: {
forcePlainText: false,
cleanPastedHTML: true
}
}),
pasteExtension = editor.getExtensionByName('paste');
selectElementContentsAndFire(editor.elements[0].firstChild);
var contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
fireEvent(this.el, 'keydown', {
keyCode: MediumEditor.util.keyCode.V,
ctrlKey: true,
metaKey: true
});
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(2);
var evt = {
type: 'paste',
preventDefault: function () {},
clipboardData: {
types: ['text/plain'],
getData: function () {
// do we need to return different results for the different types? text/plain, text/html
return 'Plain Text';
}
}
},
pasteBin = contentEditables[1];
pasteBin.innerHTML = '';
spyOn(evt, 'preventDefault');
// Paste should not be prevented on the paste bin
pasteExtension.handlePasteBinPaste(evt);
expect(evt.preventDefault).not.toHaveBeenCalled();
jasmine.clock().tick(1);
// plaintext from the clipboard should now be in the editor
expect(this.el.innerHTML).toBe('Plain Text');
// paste-bin should be gone
contentEditables = document.body.querySelectorAll('[contentEditable=true]');
expect(contentEditables.length).toBe(1);
});
});
describe('using cleanPaste', function () {

@@ -412,2 +685,3 @@ it('should filter inline rich-text', function () {

clipboardData: {
types: ['text/plain', 'text/html'],
getData: function () {

@@ -414,0 +688,0 @@ // do we need to return different results for the different types? text/plain, text/html

@@ -6,91 +6,256 @@ /*global fireEvent */

beforeEach(function () {
setupTestHelpers.call(this);
this.el = document.createElement('textarea');
this.el.className = 'editor';
this.el.value = 'test content';
this.el.setAttribute('data-disable-toolbar', false);
this.el.setAttribute('data-placeholder', 'Something');
this.el.setAttribute('data-disable-return', false);
this.el.setAttribute('data-disable-double-return', false);
this.el.setAttribute('data-disable-preview', false);
this.el.setAttribute('spellcheck', true);
this.el.setAttribute('data-imhere', 'ohyeah');
document.body.appendChild(this.el);
});
describe('MediumEditor constructor', function () {
beforeEach(function () {
setupTestHelpers.call(this);
this.el = this.createElement('textarea', 'editor');
this.el.value = 'test content';
this.el.setAttribute('data-disable-toolbar', false);
this.el.setAttribute('data-placeholder', 'Something');
this.el.setAttribute('data-disable-return', false);
this.el.setAttribute('data-disable-double-return', false);
this.el.setAttribute('data-disable-preview', false);
this.el.setAttribute('spellcheck', true);
this.el.setAttribute('data-imhere', 'ohyeah');
});
afterEach(function () {
document.body.removeChild(this.el);
this.cleanupTest();
});
afterEach(function () {
this.cleanupTest();
});
it('should accept a textarea element and "convert" it to a div, preserving all attributes', function () {
var editor = this.newMediumEditor('.editor'),
textarea = this.el;
expect(editor.elements[0].nodeName.toLowerCase()).toBe('div');
it('should accept a textarea element and "convert" it to a div, preserving all attributes', function () {
var editor = this.newMediumEditor('.editor'),
textarea = this.el;
expect(editor.elements[0].nodeName.toLowerCase()).toBe('div');
var attributes = [
'data-disable-editing',
'data-disable-toolbar',
'data-placeholder',
'data-disable-return',
'data-disable-double-return',
'data-disable-preview',
'spellcheck',
'data-imhere'
];
attributes.forEach(function (attr) {
expect(editor.elements[0].getAttribute(attr)).toBe(textarea.getAttribute(attr));
var attributes = [
'data-disable-editing',
'data-disable-toolbar',
'data-placeholder',
'data-disable-return',
'data-disable-double-return',
'data-disable-preview',
'spellcheck',
'data-imhere'
];
attributes.forEach(function (attr) {
expect(editor.elements[0].getAttribute(attr)).toBe(textarea.getAttribute(attr));
});
});
});
it('should sync editor changes with the original textarea element', function () {
var editor = this.newMediumEditor('.editor');
expect(this.el.value).toEqual('test content');
editor.elements[0].innerHTML = 'new content';
fireEvent(editor.elements[0], 'input');
fireEvent(editor.elements[0], 'keypress');
jasmine.clock().tick(1);
expect(this.el.value).toEqual('new content');
});
it('should sync editor changes with the original textarea element', function () {
var editor = this.newMediumEditor('.editor');
expect(this.el.value).toEqual('test content');
editor.elements[0].innerHTML = 'new content';
fireEvent(editor.elements[0], 'input');
fireEvent(editor.elements[0], 'keypress');
jasmine.clock().tick(1);
expect(this.el.value).toEqual('new content');
});
it('should preserver textarea className', function () {
this.el.className += ' test-class test-class-2';
var editor = this.newMediumEditor('.editor');
expect(editor.elements[0].className).toBe('editor test-class test-class-2');
});
it('should preserve textarea className', function () {
this.el.className += ' test-class test-class-2';
var editor = this.newMediumEditor('.editor');
expect(editor.elements[0].className).toBe('editor test-class test-class-2');
});
it('should create unique div ids for multiple textareas', function () {
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
}
var editor = this.newMediumEditor('.editor');
editor.elements.forEach(function (el) {
expect(document.querySelectorAll('div#' + el.id).length).toEqual(1);
it('should create unique div ids for multiple textareas', function () {
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
}
var editor = this.newMediumEditor('.editor');
editor.elements.forEach(function (el) {
expect(document.querySelectorAll('div#' + el.id).length).toEqual(1);
});
});
it('should create unique medium-editor-textarea-ids across all editor instances', function () {
var tas = [];
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
tas.push(ta);
}
tas.forEach(function (el) {
this.newMediumEditor(el);
}, this);
this.editors.forEach(function (editor) {
expect(document.querySelectorAll('textarea[medium-editor-textarea-id="' +
editor.elements[0].getAttribute('medium-editor-textarea-id') + '"]').length).toEqual(1);
});
});
it('should cleanup after destroy', function () {
var editor = this.newMediumEditor('.editor');
expect(this.el.classList.contains('medium-editor-hidden')).toBe(true);
editor.destroy();
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false);
});
});
it('should create unique medium-editor-textarea-ids across all editor instances', function () {
var tas = [];
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
tas.push(ta);
}
tas.forEach(function (el) {
this.newMediumEditor(el);
}, this);
this.editors.forEach(function (editor) {
expect(document.querySelectorAll('textarea[medium-editor-textarea-id="' +
editor.elements[0].getAttribute('medium-editor-textarea-id') + '"]').length).toEqual(1);
describe('Dynamically adding textarea elements to the editor', function () {
beforeEach(function () {
setupTestHelpers.call(this);
this.div = this.createElement('div', 'editable-div', 'sample div');
this.el = this.createElement('textarea', 'editor');
this.el.value = 'test content';
this.el.setAttribute('data-disable-toolbar', false);
this.el.setAttribute('data-placeholder', 'Something');
this.el.setAttribute('data-disable-return', false);
this.el.setAttribute('data-disable-double-return', false);
this.el.setAttribute('data-disable-preview', false);
this.el.setAttribute('spellcheck', true);
this.el.setAttribute('data-imhere', 'ohyeah');
});
afterEach(function () {
this.cleanupTest();
});
it('should "convert" it to a div, preserving all attributes', function () {
var editor = this.newMediumEditor('.editable-div'),
textarea = this.el;
expect(editor.elements[0]).toBe(this.div);
expect(editor.elements.length).toBe(1);
editor.addElements(this.el);
expect(editor.elements.length).toBe(2);
var attributes = [
'data-disable-editing',
'data-disable-toolbar',
'data-placeholder',
'data-disable-return',
'data-disable-double-return',
'data-disable-preview',
'spellcheck',
'data-imhere'
],
tempDiv = editor.elements[1];
attributes.forEach(function (attr) {
expect(tempDiv.getAttribute(attr)).toBe(textarea.getAttribute(attr));
});
});
it('should sync editor changes with the original textarea element', function () {
var editor = this.newMediumEditor('.editable-div');
editor.addElements(this.el);
expect(this.el.value).toEqual('test content');
expect(editor.elements[1].innerHTML).toEqual('test content');
editor.elements[1].innerHTML = 'new content';
fireEvent(editor.elements[1], 'input');
fireEvent(editor.elements[1], 'keypress');
jasmine.clock().tick(1);
expect(this.el.value).toEqual('new content');
});
it('should preserve textarea className', function () {
this.el.className += ' test-class test-class-2';
var editor = this.newMediumEditor('.editable-div');
editor.addElements(this.el);
expect(editor.elements[1].className).toBe('editor test-class test-class-2');
});
it('should create unique div ids for multiple textareas', function () {
this.div.setAttribute('id', 'medium-editor-12345');
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
}
var editor = this.newMediumEditor('.editable-div');
expect(editor.elements.length).toBe(1);
editor.addElements(document.querySelectorAll('.editor'));
expect(editor.elements.length).toBe(14);
editor.elements.forEach(function (el) {
expect(document.querySelectorAll('div#' + el.id).length).toEqual(1);
});
});
it('should create unique medium-editor-textarea-ids across all editor instances', function () {
var tas = [];
for (var i = 0; i < 12; i++) {
var ta = this.createElement('textarea', 'editor');
ta.value = 'test content';
tas.push(ta);
var div = this.createElement('div', 'editable-div');
div.innerHTML = 'test div';
this.newMediumEditor(div);
}
tas.forEach(function (el, idx) {
this.editors[idx].addElements(el);
}, this);
this.editors.forEach(function (editor) {
expect(document.querySelectorAll('textarea[medium-editor-textarea-id="' +
editor.elements[1].getAttribute('medium-editor-textarea-id') + '"]').length).toEqual(1);
});
});
it('should cleanup after destroy', function () {
var editor = this.newMediumEditor('.editable-div');
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false);
editor.addElements(this.el);
expect(this.el.classList.contains('medium-editor-hidden')).toBe(true);
editor.destroy();
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false);
});
});
it('should cleanup after destroy', function () {
var editor = this.newMediumEditor('.editor');
expect(this.el.classList.contains('medium-editor-hidden')).toBe(true);
editor.destroy();
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false);
describe('Calling removeElements', function () {
beforeEach(function () {
setupTestHelpers.call(this);
this.div = this.createElement('div', 'editable-div', 'sample div');
this.el = this.createElement('textarea', 'editor');
this.el.value = 'test content';
this.el.id = 'editor-textarea';
});
afterEach(function () {
this.cleanupTest();
});
it('should remove the created div and show the textarea when passed a div created for a textarea', function () {
var editor = this.newMediumEditor('.editor'),
createdDiv = editor.elements[0],
divParent = createdDiv.parentNode;
expect(editor.elements.length).toBe(1);
expect(this.el.classList.contains('medium-editor-hidden')).toBe(true);
editor.addElements(this.div);
expect(editor.elements.length).toBe(2);
editor.removeElements(createdDiv);
expect(editor.elements.length).toBe(1);
expect(createdDiv.parentNode).not.toBe(divParent, 'The div created for the textarea should have been removed');
expect(this.el.hasAttribute('medium-editor-textarea-id')).toBe(false,
'The textarea should not have a medium-editor-textarea-id attribute when its editor div is removed');
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false,
'The textarea should be hidden when its editor div is removed');
});
it('should remove the created div and show the textaarea when passed the actual textarea', function () {
var editor = this.newMediumEditor('.editor'),
createdDiv = editor.elements[0],
divParent = createdDiv.parentNode;
expect(editor.elements.length).toBe(1);
expect(this.el.classList.contains('medium-editor-hidden')).toBe(true);
editor.addElements(this.div);
expect(editor.elements.length).toBe(2);
editor.removeElements(this.el);
expect(editor.elements.length).toBe(1);
expect(createdDiv.parentNode).not.toBe(divParent, 'The div created for the textarea should have been removed');
expect(this.el.hasAttribute('medium-editor-textarea-id')).toBe(false,
'The textarea should not have a medium-editor-textarea-id attribute when its editor div is removed');
expect(this.el.classList.contains('medium-editor-hidden')).toBe(false,
'The textarea should be hidden when its editor div is removed');
});
});
});

@@ -175,2 +175,9 @@ (function () {

function handleEditableInput(event, editable) {
var textarea = editable.parentNode.querySelector('textarea[medium-editor-textarea-id="' + editable.getAttribute('medium-editor-textarea-id') + '"]');
if (textarea) {
textarea.value = editable.innerHTML.trim();
}
}
// Internal helper methods which shouldn't be exposed externally

@@ -209,3 +216,5 @@

function createElementsArray(selector) {
function createElementsArray(selector, doc, filterEditorElements) {
var elements = [];
if (!selector) {

@@ -216,3 +225,3 @@ selector = [];

if (typeof selector === 'string') {
selector = this.options.ownerDocument.querySelectorAll(selector);
selector = doc.querySelectorAll(selector);
}

@@ -223,16 +232,34 @@ // If element, put into array

}
// Convert NodeList (or other array like object) into an array
var elements = Array.prototype.slice.apply(selector);
// Loop through elements and convert textarea's into divs
this.elements = [];
elements.forEach(function (element, index) {
if (element.nodeName.toLowerCase() === 'textarea') {
this.elements.push(createContentEditable.call(this, element, index));
} else {
this.elements.push(element);
if (filterEditorElements) {
// Remove elements that have already been initialized by the editor
// selecotr might not be an array (ie NodeList) so use for loop
for (var i = 0; i < selector.length; i++) {
var el = selector[i];
if (MediumEditor.util.isElement(el) &&
!el.getAttribute('data-medium-editor-element') &&
!el.getAttribute('medium-editor-textarea-id')) {
elements.push(el);
}
}
}, this);
} else {
// Convert NodeList (or other array like object) into an array
elements = Array.prototype.slice.apply(selector);
}
return elements;
}
function cleanupTextareaElement(element) {
var textarea = element.parentNode.querySelector('textarea[medium-editor-textarea-id="' + element.getAttribute('medium-editor-textarea-id') + '"]');
if (textarea) {
// Un-hide the textarea
textarea.classList.remove('medium-editor-hidden');
textarea.removeAttribute('medium-editor-textarea-id');
}
if (element.parentNode) {
element.parentNode.removeChild(element);
}
}
function setExtensionDefaults(extension, defaults) {

@@ -313,4 +340,4 @@ Object.keys(defaults).forEach(function (prop) {

function createContentEditable(textarea, id) {
var div = this.options.ownerDocument.createElement('div'),
function createContentEditable(textarea, id, doc) {
var div = doc.createElement('div'),
now = Date.now(),

@@ -322,3 +349,3 @@ uniqueId = 'medium-editor-' + now + '-' + id,

// to make a unique-id, ensure that the id is actually unique on the page
while (this.options.ownerDocument.getElementById(uniqueId)) {
while (doc.getElementById(uniqueId)) {
now++;

@@ -351,6 +378,14 @@ uniqueId = 'medium-editor-' + now + '-' + id;

function initElements() {
var isTextareaUsed = false;
function initElement(element, id) {
if (!element.getAttribute('data-medium-editor-element')) {
if (element.nodeName.toLowerCase() === 'textarea') {
element = createContentEditable(element, id, this.options.ownerDocument);
this.elements.forEach(function (element, index) {
// Make sure we only attach to editableInput once for <textarea> elements
if (!this.instanceHandleEditableInput) {
this.instanceHandleEditableInput = handleEditableInput.bind(this);
this.subscribe('editableInput', this.instanceHandleEditableInput);
}
}
if (!this.options.disableEditing && !element.getAttribute('data-disable-editing')) {

@@ -360,25 +395,29 @@ element.setAttribute('contentEditable', true);

}
// Make sure we only attach to editableKeydownEnter once for disable-return options
if (!this.instanceHandleEditableKeydownEnter) {
if (element.getAttribute('data-disable-return') || element.getAttribute('data-disable-double-return')) {
this.instanceHandleEditableKeydownEnter = handleDisabledEnterKeydown.bind(this);
this.subscribe('editableKeydownEnter', this.instanceHandleEditableKeydownEnter);
}
}
// if we're not disabling return, add a handler to help handle cleanup
// for certain cases when enter is pressed
if (!this.options.disableReturn && !element.getAttribute('data-disable-return')) {
this.on(element, 'keyup', handleKeyup.bind(this));
}
element.setAttribute('data-medium-editor-element', true);
element.setAttribute('role', 'textbox');
element.setAttribute('aria-multiline', true);
element.setAttribute('medium-editor-index', index);
element.setAttribute('medium-editor-index', MediumEditor.util.guid());
if (element.hasAttribute('medium-editor-textarea-id')) {
isTextareaUsed = true;
}
}, this);
this.events.attachAllEventsToElement(element);
}
if (isTextareaUsed) {
this.subscribe('editableInput', function (event, editable) {
var textarea = editable.parentNode.querySelector('textarea[medium-editor-textarea-id="' + editable.getAttribute('medium-editor-textarea-id') + '"]');
if (textarea) {
textarea.value = this.serialize()[editable.id].value;
}
}.bind(this));
}
return element;
}
function attachHandlers() {
var i;
// attach to tabs

@@ -396,23 +435,10 @@ this.subscribe('editableKeydownTab', handleTabKeydown.bind(this));

// disabling return or double return
if (this.options.disableReturn || this.options.disableDoubleReturn) {
this.subscribe('editableKeydownEnter', handleDisabledEnterKeydown.bind(this));
} else {
for (i = 0; i < this.elements.length; i += 1) {
if (this.elements[i].getAttribute('data-disable-return') || this.elements[i].getAttribute('data-disable-double-return')) {
this.subscribe('editableKeydownEnter', handleDisabledEnterKeydown.bind(this));
break;
}
// Make sure we only attach to editableKeydownEnter once for disable-return options
if (!this.instanceHandleEditableKeydownEnter) {
// disabling return or double return
if (this.options.disableReturn || this.options.disableDoubleReturn) {
this.instanceHandleEditableKeydownEnter = handleDisabledEnterKeydown.bind(this);
this.subscribe('editableKeydownEnter', this.instanceHandleEditableKeydownEnter);
}
}
// if we're not disabling return, add a handler to help handle cleanup
// for certain cases when enter is pressed
if (!this.options.disableReturn) {
this.elements.forEach(function (element) {
if (!element.getAttribute('data-disable-return')) {
this.on(element, 'keyup', handleKeyup.bind(this));
}
}, this);
}
}

@@ -614,4 +640,7 @@

createElementsArray.call(this, this.origElements);
this.events = new MediumEditor.Events(this);
this.elements = [];
this.addElements(this.origElements);
if (this.elements.length === 0) {

@@ -624,6 +653,3 @@ return;

this.events = new MediumEditor.Events(this);
// Call initialization helpers
initElements.call(this);
initExtensions.call(this);

@@ -663,14 +689,9 @@ attachHandlers.call(this);

// Remove any elements created for textareas
if (element.hasAttribute('medium-editor-textarea-id')) {
var textarea = element.parentNode.querySelector('textarea[medium-editor-textarea-id="' + element.getAttribute('medium-editor-textarea-id') + '"]');
if (textarea) {
// Un-hide the textarea
textarea.classList.remove('medium-editor-hidden');
}
if (element.parentNode) {
element.parentNode.removeChild(element);
}
if (element.getAttribute('medium-editor-textarea-id')) {
cleanupTextareaElement(element);
}
}, this);
this.elements = [];
this.instanceHandleEditableKeydownEnter = null;
this.instanceHandleEditableInput = null;

@@ -722,4 +743,6 @@ removeFromEditors.call(this, this.options.contentWindow);

elementid,
content = {};
for (i = 0; i < this.elements.length; i += 1) {
content = {},
len = this.elements.length;
for (i = 0; i < len; i += 1) {
elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;

@@ -1089,3 +1112,3 @@ content[elementid] = {

customEvent.initEvent('input', true, true, this.options.contentWindow);
for (var i = 0; i < this.elements.length; i += 1) {
for (var i = 0, len = this.elements.length; i < len; i += 1) {
this.elements[i].dispatchEvent(customEvent);

@@ -1115,6 +1138,54 @@ }

target.innerHTML = html;
this.events.updateInput(target, { target: target, currentTarget: target });
this.checkContentChanged(target);
}
},
checkContentChanged: function (editable) {
editable = editable || MediumEditor.selection.getSelectionElement(this.options.contentWindow);
this.events.updateInput(editable, { target: editable, currentTarget: editable });
},
addElements: function (selector) {
// Convert elements into an array
var elements = createElementsArray(selector, this.options.ownerDocument, true);
// Do we have elements to add now?
if (elements.length === 0) {
return false;
}
elements.forEach(function (element) {
// Initialize all new elements (we check that in those functions don't worry)
element = initElement.call(this, element);
// Add new elements to our internal elements array
this.elements.push(element);
}, this);
},
removeElements: function (selector) {
// Convert elements into an array
var elements = createElementsArray(selector, this.options.ownerDocument),
toRemove = elements.map(function (el) {
// For textareas, make sure we're looking at the editor div and not the textarea itself
if (el.getAttribute('medium-editor-textarea-id') && el.parentNode) {
return el.parentNode.querySelector('div[medium-editor-textarea-id="' + el.getAttribute('medium-editor-textarea-id') + '"]');
} else {
return el;
}
});
this.elements = this.elements.filter(function (element) {
// If this is an element we want to remove
if (toRemove.indexOf(element) !== -1) {
this.events.cleanupElement(element);
if (element.getAttribute('medium-editor-textarea-id')) {
cleanupTextareaElement(element);
}
return false;
}
return true;
}, this);
}
};
}());

@@ -59,2 +59,26 @@ (function () {

detachAllEventsFromElement: function (element) {
var filtered = this.events.filter(function (e) {
return e && e[0].getAttribute && e[0].getAttribute('medium-editor-index') === element.getAttribute('medium-editor-index');
});
for (var i = 0, len = filtered.length; i < len; i++) {
var e = filtered[i];
this.detachDOMEvent(e[0], e[1], e[2], e[3]);
}
},
// Attach all existing handlers to a new element
attachAllEventsToElement: function (element) {
if (this.listeners['editableInput']) {
this.contentCache[element.getAttribute('medium-editor-index')] = element.innerHTML;
}
if (this.eventsCache) {
this.eventsCache.forEach(function (e) {
this.attachDOMEvent(element, e['name'], e['handler'].bind(this));
}, this);
}
},
enableCustomEvent: function (event) {

@@ -251,11 +275,11 @@ if (this.disabledEvents[event] !== undefined) {

// setup cache for knowing when the content has changed
this.contentCache = [];
this.contentCache = {};
this.base.elements.forEach(function (element) {
this.contentCache[element.getAttribute('medium-editor-index')] = element.innerHTML;
}, this);
// Attach to the 'oninput' event, handled correctly by most browsers
if (this.InputEventOnContenteditableSupported) {
this.attachDOMEvent(element, 'input', this.handleInput.bind(this));
}
}.bind(this));
// Attach to the 'oninput' event, handled correctly by most browsers
if (this.InputEventOnContenteditableSupported) {
this.attachToEachElement('input', this.handleInput);
}

@@ -330,7 +354,22 @@ // For browsers which don't support the input event on contenteditable (IE)

attachToEachElement: function (name, handler) {
// build our internal cache to know which element got already what handler attached
if (!this.eventsCache) {
this.eventsCache = [];
}
this.base.elements.forEach(function (element) {
this.attachDOMEvent(element, name, handler.bind(this));
}, this);
this.eventsCache.push({ 'name': name, 'handler': handler });
},
cleanupElement: function (element) {
var index = element.getAttribute('medium-editor-index');
if (index) {
this.detachAllEventsFromElement(element);
delete this.contentCache[index];
}
},
focusElement: function (element) {

@@ -337,0 +376,0 @@ element.focus();

(function () {
'use strict';
/* Helpers and internal variables that don't need to be members of actual paste handler */
var pasteBinDefaultContent = '%ME_PASTEBIN%',
lastRange = null,
keyboardPasteEditable = null,
stopProp = function (event) {
event.stopPropagation();
};
/*jslint regexp: true*/

@@ -12,2 +22,11 @@ /*

return [
// Remove anything but the contents within the BODY element
[new RegExp(/^[\s\S]*<body[^>]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g), ''],
// cleanup comments added by Chrome when pasting html
[new RegExp(/<!--StartFragment-->|<!--EndFragment-->/g), ''],
// Trailing BR elements
[new RegExp(/<br>$/i), ''],
// replace two bogus tags that begin pastes from google docs

@@ -44,5 +63,4 @@ [new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ''],

// cleanup comments added by Chrome when pasting html
['<!--EndFragment-->', ''],
['<!--StartFragment-->', '']
// Microsoft Word adds some special elements around list items
[new RegExp(/<!\[if !supportLists\]>(((?!<!).)*)<!\[endif]\>/gi), '$1']
];

@@ -52,2 +70,37 @@ }

/**
* Gets various content types out of the Clipboard API. It will also get the
* plain text using older IE and WebKit API.
*
* @param {event} event Event fired on paste.
* @param {win} reference to window
* @param {doc} reference to document
* @return {Object} Object with mime types and data for those mime types.
*/
function getClipboardContent(event, win, doc) {
var dataTransfer = event.clipboardData || win.clipboardData || doc.dataTransfer,
data = {};
if (!dataTransfer) {
return data;
}
// Use old WebKit/IE API
if (dataTransfer.getData) {
var legacyText = dataTransfer.getData('Text');
if (legacyText && legacyText.length > 0) {
data['text/plain'] = legacyText;
}
}
if (dataTransfer.types) {
for (var i = 0; i < dataTransfer.types.length; i++) {
var contentType = dataTransfer.types[i];
data[contentType] = dataTransfer.getData(contentType);
}
}
return data;
}
var PasteHandler = MediumEditor.Extension.extend({

@@ -97,16 +150,23 @@ /* Paste Options */

this.subscribe('editablePaste', this.handlePaste.bind(this));
this.subscribe('editableKeydown', this.handleKeydown.bind(this));
}
},
handlePaste: function (event, element) {
var paragraphs,
html = '',
p,
dataFormatHTML = 'text/html',
dataFormatPlain = 'text/plain',
pastedHTML,
pastedPlain;
destroy: function () {
// Make sure pastebin is destroyed in case it's still around for some reason
if (this.forcePlainText || this.cleanPastedHTML) {
this.removePasteBin();
}
},
if (this.window.clipboardData && event.clipboardData === undefined) {
event.clipboardData = this.window.clipboardData;
handlePaste: function (event, editable) {
if (event.defaultPrevented) {
return;
}
var clipboardContent = getClipboardContent(event, this.window, this.document),
pastedHTML = clipboardContent['text/html'],
pastedPlain = clipboardContent['text/plain'];
if (this.window.clipboardData && event.clipboardData === undefined && !pastedHTML) {
// If window.clipboardData exists, but event.clipboardData doesn't exist,

@@ -116,38 +176,185 @@ // we're probably in IE. IE only has two possibilities for clipboard

//
// Of the two, we want 'Text':
dataFormatHTML = 'Text';
dataFormatPlain = 'Text';
// For IE, we'll fallback to 'Text' for text/html
pastedHTML = pastedPlain;
}
if (event.clipboardData &&
event.clipboardData.getData &&
!event.defaultPrevented) {
if (pastedHTML || pastedPlain) {
event.preventDefault();
pastedHTML = event.clipboardData.getData(dataFormatHTML);
pastedPlain = event.clipboardData.getData(dataFormatPlain);
this.doPaste(pastedHTML, pastedPlain, editable);
}
},
if (this.cleanPastedHTML && pastedHTML) {
return this.cleanPaste(pastedHTML);
}
doPaste: function (pastedHTML, pastedPlain, editable) {
var paragraphs,
html = '',
p;
if (!(this.getEditorOption('disableReturn') || element.getAttribute('data-disable-return'))) {
paragraphs = pastedPlain.split(/[\r\n]+/g);
// If there are no \r\n in data, don't wrap in <p>
if (paragraphs.length > 1) {
for (p = 0; p < paragraphs.length; p += 1) {
if (paragraphs[p] !== '') {
html += '<p>' + MediumEditor.util.htmlEntities(paragraphs[p]) + '</p>';
}
if (this.cleanPastedHTML && pastedHTML) {
return this.cleanPaste(pastedHTML);
}
if (!(this.getEditorOption('disableReturn') || (editable && editable.getAttribute('data-disable-return')))) {
paragraphs = pastedPlain.split(/[\r\n]+/g);
// If there are no \r\n in data, don't wrap in <p>
if (paragraphs.length > 1) {
for (p = 0; p < paragraphs.length; p += 1) {
if (paragraphs[p] !== '') {
html += '<p>' + MediumEditor.util.htmlEntities(paragraphs[p]) + '</p>';
}
} else {
html = MediumEditor.util.htmlEntities(paragraphs[0]);
}
} else {
html = MediumEditor.util.htmlEntities(pastedPlain);
html = MediumEditor.util.htmlEntities(paragraphs[0]);
}
MediumEditor.util.insertHTMLCommand(this.document, html);
} else {
html = MediumEditor.util.htmlEntities(pastedPlain);
}
MediumEditor.util.insertHTMLCommand(this.document, html);
},
handlePasteBinPaste: function (event) {
if (event.defaultPrevented) {
this.removePasteBin();
return;
}
var clipboardContent = getClipboardContent(event, this.window, this.document),
pastedHTML = clipboardContent['text/html'],
pastedPlain = clipboardContent['text/plain'],
editable = keyboardPasteEditable;
// If we have valid html already, or we're not in cleanPastedHTML mode
// we can ignore the paste bin and just paste now
if (!this.cleanPastedHTML || pastedHTML) {
event.preventDefault();
this.removePasteBin();
this.doPaste(pastedHTML, pastedPlain, editable);
return;
}
// We need to look at the paste bin, so do a setTimeout to let the paste
// fall through into the paste bin
setTimeout(function () {
// Only look for HTML if we're in cleanPastedHTML mode
if (this.cleanPastedHTML) {
// If clipboard didn't have HTML, try the paste bin
pastedHTML = this.getPasteBinHtml();
}
// If we needed the paste bin, we're done with it now, remove it
this.removePasteBin();
// Handle the paste with the html from the paste bin
this.doPaste(pastedHTML, pastedPlain, editable);
}.bind(this), 0);
},
handleKeydown: function (event, editable) {
// if it's not Ctrl+V, do nothing
if (!(MediumEditor.util.isKey(event, MediumEditor.util.keyCode.V) && MediumEditor.util.isMetaCtrlKey(event))) {
return;
}
event.stopImmediatePropagation();
this.removePasteBin();
this.createPasteBin(editable);
},
createPasteBin: function (editable) {
var rects,
range = MediumEditor.selection.getSelectionRange(this.document),
top = this.window.pageYOffset;
keyboardPasteEditable = editable;
if (range) {
rects = range.getClientRects();
// on empty line, rects is empty so we grab information from the first container of the range
if (rects.length) {
top += rects[0].top;
} else {
top += range.startContainer.getBoundingClientRect().top;
}
}
lastRange = range;
var pasteBinElm = this.document.createElement('div');
pasteBinElm.id = this.pasteBinId = 'medium-editor-pastebin-' + (+Date.now());
pasteBinElm.setAttribute('style', 'border: 1px red solid; position: absolute; top: ' + top + 'px; width: 10px; height: 10px; overflow: hidden; opacity: 0');
pasteBinElm.setAttribute('contentEditable', true);
pasteBinElm.innerHTML = pasteBinDefaultContent;
this.document.body.appendChild(pasteBinElm);
// avoid .focus() to stop other event (actually the paste event)
this.on(pasteBinElm, 'focus', stopProp);
this.on(pasteBinElm, 'focusin', stopProp);
this.on(pasteBinElm, 'focusout', stopProp);
pasteBinElm.focus();
MediumEditor.selection.selectNode(pasteBinElm, this.document);
if (!this.boundHandlePaste) {
this.boundHandlePaste = this.handlePasteBinPaste.bind(this);
}
this.on(pasteBinElm, 'paste', this.boundHandlePaste);
},
removePasteBin: function () {
if (null !== lastRange) {
MediumEditor.selection.selectRange(this.document, lastRange);
lastRange = null;
}
if (null !== keyboardPasteEditable) {
keyboardPasteEditable = null;
}
var pasteBinElm = this.getPasteBin();
if (!pasteBinElm) {
return;
}
if (pasteBinElm) {
this.off(pasteBinElm, 'focus', stopProp);
this.off(pasteBinElm, 'focusin', stopProp);
this.off(pasteBinElm, 'focusout', stopProp);
this.off(pasteBinElm, 'paste', this.boundHandlePaste);
pasteBinElm.parentElement.removeChild(pasteBinElm);
}
},
getPasteBin: function () {
return this.document.getElementById(this.pasteBinId);
},
getPasteBinHtml: function () {
var pasteBinElm = this.getPasteBin();
if (!pasteBinElm) {
return false;
}
// WebKit has a nice bug where it clones the paste bin if you paste from for example notepad
// so we need to force plain text mode in this case
if (pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') {
return false;
}
var pasteBinHtml = pasteBinElm.innerHTML;
// If paste bin is empty try using plain text mode
// since that is better than nothing right
if (!pasteBinHtml || pasteBinHtml === pasteBinDefaultContent) {
return false;
}
return pasteBinHtml;
},
cleanPaste: function (text) {

@@ -229,2 +436,3 @@ var i, elList, tmp, workEl,

// TODO (6.0): Make this an internal helper instead of member of paste handler
isCommonBlock: function (el) {

@@ -234,2 +442,3 @@ return (el && (el.nodeName.toLowerCase() === 'p' || el.nodeName.toLowerCase() === 'div'));

// TODO (6.0): Make this an internal helper instead of member of paste handler
filterCommonBlocks: function (el) {

@@ -241,2 +450,3 @@ if (/^\s*$/.test(el.textContent) && el.parentNode) {

// TODO (6.0): Make this an internal helper instead of member of paste handler
filterLineBreak: function (el) {

@@ -255,2 +465,3 @@ if (this.isCommonBlock(el.previousElementSibling)) {

// TODO (6.0): Make this an internal helper instead of member of paste handler
// remove an element, including its parent, if it is the only element within its parent

@@ -267,2 +478,3 @@ removeWithParent: function (el) {

// TODO (6.0): Make this an internal helper instead of member of paste handler
cleanupSpans: function (containerEl) {

@@ -269,0 +481,0 @@ var i,

@@ -20,3 +20,3 @@ # Walkthrough - Building a Button

To interact with the demo, load the page from your fork in a browser via:
To interact with the demo, load the page from your fork in a browser via:

@@ -50,3 +50,3 @@ `file://[Medium Editor Source Root]/demo/button-example.html`

**NOTE:**
**NOTE:**

@@ -146,3 +146,3 @@ In order for the toolbar to look for a button to add to the toolbar, the name of the extension must be passed in the `toolbar.buttons` array.

});
this.button = this.document.createElement('button');

@@ -162,2 +162,6 @@ this.button.classList.add('medium-editor-action');

this.classApplier.toggleSelection();
// Ensure the editor knows about an html change so watchers are notified
// ie: <textarea> elements depend on the editableInput event to stay synchronized
this.base.checkContentChanged();
}

@@ -182,6 +186,10 @@ });

**NOTE:**
**NOTE:**
A great convienience of using the `toggleSelection()` method of the **CSS Class Applier** is that it will also unwrap the selection. So, since we're always calling `toggleSelection()` when the button is clicked, if you highlight the same text and click the button again, the text will go back to normal and the `<mark>` element will be removed.
**NOTE:**
In `handleClick` the extra call to `this.base.checkContentChanged` is calling the core editor directly to notify it that the html may have changed. This is needed here so that any external dependencies that are watching for changes the HTML are notified. For example, you can pass a `<textarea>` as a editor element when initializing MediumEditor and the editor relies on knowing about changes to the html so it can keep the `<textarea>` synchronized with changes to the generated `<div>` that is displayed as the actual editor element.
***

@@ -204,3 +212,3 @@ ### 5. Respond to Selection

});
this.button = this.document.createElement('button');

@@ -220,2 +228,3 @@ this.button.classList.add('medium-editor-action');

this.classApplier.toggleSelection();
this.base.checkContentChanged();
},

@@ -252,3 +261,3 @@

1. **isActive()**
* This should return whether the button is already active. We check this by seeing if the `'medium-editor-button-active'` class already exists on the toolbar button.
* This should return whether the button is already active. We check this by seeing if the `'medium-editor-button-active'` class already exists on the toolbar button.
1. **setActive()**

@@ -289,2 +298,3 @@ * This is called when we should make our button active (add the '`medium-editor-button-active'` class)

this.classApplier.toggleSelection();
this.base.checkContentChanged();
}

@@ -308,2 +318,2 @@ });

1. **isActive()**, **setActive()**, and **setInactive()**
* The default button extension implements each of these methods, using whatever css class is configured as the `activeButtonClass` in MediumEditor (`'medium-editor-button-active'` by default)
* The default button extension implements each of these methods, using whatever css class is configured as the `activeButtonClass` in MediumEditor (`'medium-editor-button-active'` by default)

@@ -216,5 +216,3 @@ (function () {

var sel = doc.getSelection();
sel.removeAllRanges();
sel.addRange(range);
this.selectRange(doc, range);
},

@@ -612,12 +610,8 @@

selectNode: function (node, doc) {
var range = doc.createRange(),
sel = doc.getSelection();
var range = doc.createRange();
range.selectNodeContents(node);
sel.removeAllRanges();
sel.addRange(range);
this.selectRange(doc, range);
},
select: function (doc, startNode, startOffset, endNode, endOffset) {
doc.getSelection().removeAllRanges();
var range = doc.createRange();

@@ -630,3 +624,3 @@ range.setStart(startNode, startOffset);

}
doc.getSelection().addRange(range);
this.selectRange(doc, range);
return range;

@@ -668,2 +662,9 @@ },

selectRange: function (ownerDocument, range) {
var selection = ownerDocument.getSelection();
selection.removeAllRanges();
selection.addRange(range);
},
// http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi

@@ -670,0 +671,0 @@ // by You

@@ -52,2 +52,3 @@ /*global NodeFilter*/

// https://github.com/jashkenas/underscore
// Lonely letter MUST USE the uppercase code
keyCode: {

@@ -61,3 +62,4 @@ BACKSPACE: 8,

K: 75, // K keycode, and not k
M: 77
M: 77,
V: 86
},

@@ -497,4 +499,3 @@

range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
MediumEditor.selection.selectRange(doc, range);
}

@@ -1058,2 +1059,13 @@ res = true;

}
},
guid: function () {
function _s4() {
return Math
.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return _s4() + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + '-' + _s4() + _s4() + _s4();
}

@@ -1060,0 +1072,0 @@ };

@@ -18,3 +18,3 @@ MediumEditor.parseVersionString = function (release) {

// grunt-bump looks for this:
'version': '5.16.1'
'version': '5.17.0'
}).version);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc