medium-editor
Advanced tools
Comparing version 5.22.2 to 5.23.0
@@ -0,1 +1,50 @@ | ||
5.23.0 / 2017-03-02 | ||
================== | ||
* Only add schemes to URLs with hostnames #1258 | ||
* Fix problem with addClassToAnchors #1293 | ||
* Adding new 'html' button from #1235 #1291 | ||
* Don't encode fragment as part of linkValidation #1257 | ||
5.22.2 / 2017-01-19 | ||
================== | ||
* Efficiency: Compile RegEx once #1230 | ||
* Error in console at link selection #1249 | ||
* Check for this.anchorPreview when hiding #1280 | ||
* Save some CPU calculations #1271 | ||
5.22.1 / 2016-09-29 | ||
================== | ||
* Fix encoded urls (in linkValidaton) #1219 | ||
* Fix CommonJS environment #1221 | ||
5.22.0 / 2016-09-03 | ||
================== | ||
* Add new extensions to extensions README #1188 | ||
* Fix iframe div #1179 | ||
* Fix placeholder text color in flat theme #1192 | ||
* Add unwrapTags option to paste extension #1177 | ||
* Remove first empty paragraph on backspace #1187 | ||
* Update grunt-contrib-jasmine #1185 | ||
* Added Embed Button links to README #1183 | ||
5.21.1 / 2016-08-11 | ||
================== | ||
* Make linkValidation allow hash links #1143 | ||
* Fix toolbar in absolute container #1152 | ||
* Fix caret issue in blockquote #1162 | ||
* Handle new Google Docs font weights #1168 | ||
* Add external button example #1175 | ||
5.21.0 / 2016-06-21 | ||
================== | ||
* Fix issue with electron environment #1125 | ||
* Fix for paste and placeholder extensions & add/remove element events #1124 | ||
* Placeholder is visible when only empty table is in Editor #1128 | ||
5.20.2 / 2016-06-17 | ||
@@ -2,0 +51,0 @@ ================== |
{ | ||
"name": "medium-editor", | ||
"version": "5.22.2", | ||
"version": "5.23.0", | ||
"author": "Davi Ferreira <hi@daviferreira.com>", | ||
@@ -43,2 +43,3 @@ "contributors": [ | ||
"brfs": "1.4.2", | ||
"connect": "3.5.0", | ||
"grunt": "0.4.5", | ||
@@ -65,2 +66,3 @@ "grunt-autoprefixer": "3.0.3", | ||
"lodash": "3.10.1", | ||
"serve-static": "1.11.2", | ||
"time-grunt": "1.3.0" | ||
@@ -67,0 +69,0 @@ }, |
# MediumEditor | ||
[![Join the chat at https://gitter.im/yabwe/medium-editor](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/yabwe/medium-editor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
This is a clone of [medium.com](https://medium.com) inline editor toolbar. | ||
@@ -9,5 +7,9 @@ | ||
[![screenshot](https://raw.github.com/yabwe/medium-editor/master/demo/img/medium-editor.jpg)](http://yabwe.github.io/medium-editor/) | ||
[![Join the chat at https://gitter.im/yabwe/medium-editor](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/yabwe/medium-editor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
## Browser Support | ||
[![Sauce Test Status](https://saucelabs.com/browser-matrix/mediumeditor.svg)](https://saucelabs.com/u/mediumeditor) | ||
[![Saucelabs Build Status](https://saucelabs.com/browser-matrix/mediumeditor.svg)](https://saucelabs.com/beta/dashboard/builds) | ||
@@ -25,3 +27,3 @@ ![Supported Browsers](https://cloud.githubusercontent.com/assets/2444240/12874138/d3960a04-cd9b-11e5-8cc5-8136d82cf5f6.png) | ||
![screenshot](https://raw.github.com/yabwe/medium-editor/master/demo/img/medium-editor.jpg) | ||
### Demo | ||
@@ -64,2 +66,4 @@ __demo__: [http://yabwe.github.io/medium-editor/](http://yabwe.github.io/medium-editor/) | ||
Find the files to below mentioned linking in the dist folder. (./medium-editor/dist/...) | ||
```html | ||
@@ -520,2 +524,3 @@ <link rel="stylesheet" href="css/medium-editor.css"> <!-- Core --> | ||
* __removeFormat__ (clears inline style formatting, preserves blocks) | ||
* __html__ (parses selected html and converts into actual html elements) | ||
@@ -654,2 +659,9 @@ ## Themes | ||
To run the demo locally: | ||
1. Clone this repo locally | ||
2. Run `npm install` from your console at the root | ||
3. Run `node index.js` from the root | ||
4. Navigate to `http://localhost:8088/demo/index.html` to view the demo | ||
MediumEditor development tasks are managed by Grunt. To install all the necessary packages, just invoke: | ||
@@ -656,0 +668,0 @@ |
@@ -359,2 +359,59 @@ /*global fireEvent, selectElementContents, | ||
it('should not add a scheme to an absolute path', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
linkValidation: true | ||
} | ||
}), | ||
absolutePath = '/test', | ||
link, | ||
anchorExtension = editor.getExtensionByName('anchor'); | ||
selectElementContentsAndFire(editor.elements[0]); | ||
anchorExtension.showForm(absolutePath); | ||
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click'); | ||
link = editor.elements[0].querySelector('a'); | ||
expect(link).not.toBeNull(); | ||
expect(link.getAttribute('href')).toBe(absolutePath); | ||
}); | ||
it('should not add a scheme to an obviously relative path', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
linkValidation: true | ||
} | ||
}), | ||
relativePath = 'test/file.html', | ||
link, | ||
anchorExtension = editor.getExtensionByName('anchor'); | ||
selectElementContentsAndFire(editor.elements[0]); | ||
anchorExtension.showForm(relativePath); | ||
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click'); | ||
link = editor.elements[0].querySelector('a'); | ||
expect(link).not.toBeNull(); | ||
expect(link.getAttribute('href')).toBe(relativePath); | ||
}); | ||
it('should add a scheme to a localhost url', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
linkValidation: true | ||
} | ||
}), | ||
localhostUrl = 'http://localhost', | ||
link, | ||
anchorExtension = editor.getExtensionByName('anchor'); | ||
selectElementContentsAndFire(editor.elements[0]); | ||
anchorExtension.showForm('localhost'); | ||
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click'); | ||
link = editor.elements[0].querySelector('a'); | ||
expect(link).not.toBeNull(); | ||
expect(link.getAttribute('href')).toBe(localhostUrl); | ||
}); | ||
it('should change spaces to %20 for a valid url if linkValidation option is set to true', function () { | ||
@@ -386,2 +443,28 @@ var editor = this.newMediumEditor('.editor', { | ||
it('should not change # to %23 for a valid url if linkValidation option is set to true', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
linkValidation: true | ||
} | ||
}), | ||
link, | ||
anchorExtension = editor.getExtensionByName('anchor'), | ||
expectedOpts = { | ||
value: 'http://example.com/?query=value#anchor', | ||
target: '_self' | ||
}; | ||
spyOn(editor, 'execAction').and.callThrough(); | ||
selectElementContentsAndFire(editor.elements[0]); | ||
anchorExtension.showForm(expectedOpts.value); | ||
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click'); | ||
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts); | ||
link = editor.elements[0].querySelector('a'); | ||
expect(link).not.toBeNull(); | ||
expect(link.href).toBe(expectedOpts.value); | ||
}); | ||
it('should not encode an encoded URL if linkValidation option is set to true', function () { | ||
@@ -574,9 +657,55 @@ var editor = this.newMediumEditor('.editor', { | ||
it('should create a button when user selects this option, even if selected text carries tags ' + | ||
'and the selection doesn\'t exactly match those tags', function () { | ||
spyOn(MediumEditor.prototype, 'createLink').and.callThrough(); | ||
this.el.innerHTML = '<p>Hello <b>world</b> !</p>'; | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
customClassOption: 'btn btn-default' | ||
} | ||
}), | ||
p = this.el.querySelector('p'), | ||
b = p.childNodes[1], | ||
emptySpacePart = p.childNodes[2], | ||
save, | ||
input, | ||
button, | ||
link, | ||
opts, | ||
anchorExtension = editor.getExtensionByName('anchor'), | ||
toolbar = editor.getExtensionByName('toolbar'); | ||
MediumEditor.selection.select(document, b, 0, emptySpacePart, 1); //select world including space | ||
save = toolbar.getToolbarElement().querySelector('[data-action="createLink"]'); | ||
input = anchorExtension.getInput(); | ||
input.value = 'http://test.com'; | ||
button = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-button'); | ||
button.setAttribute('type', 'checkbox'); | ||
button.checked = true; | ||
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click'); | ||
opts = { | ||
value: 'http://test.com', | ||
target: '_self', | ||
buttonClass: 'btn btn-default' | ||
}; | ||
expect(editor.createLink).toHaveBeenCalledWith(opts); | ||
link = editor.elements[0].querySelector('a'); | ||
expect(link).not.toBeNull('Link does not exist'); | ||
expect(link.classList.contains('btn')).toBe(true, 'Link does not contain class btn'); | ||
expect(link.classList.contains('btn-default')).toBe(true, 'Link does not contain class btn-default'); | ||
}); | ||
it('should remove the target _blank from the anchor tag when the open in a new window checkbox,' + | ||
' is unchecked and the form is saved', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
anchor: { | ||
targetCheckbox: true | ||
} | ||
}), | ||
anchor: { | ||
targetCheckbox: true | ||
} | ||
}), | ||
anchorExtension = editor.getExtensionByName('anchor'), | ||
@@ -583,0 +712,0 @@ targetCheckbox, |
@@ -747,2 +747,22 @@ /*global fireEvent, selectElementContentsAndFire, | ||
describe('Html', function () { | ||
it('should create an evaluated html tag', function () { | ||
var editor = this.newMediumEditor('.editor', { | ||
toolbar: { | ||
buttons: ['html'] | ||
} | ||
}), | ||
toolbar = editor.getExtensionByName('toolbar'), | ||
button = toolbar.getToolbarElement().querySelector('[data-action="html"]'); | ||
spyOn(document, 'execCommand').and.callThrough(); | ||
this.el.innerHTML = '<span id="span-html"><iframe width="854" height="480" src="https://www.youtube.com/embed/QHH3iSeDBLo" frameborder="0" allowfullscreen></iframe> \n\n</span>'; | ||
selectElementContentsAndFire(document.getElementById('span-html')); | ||
fireEvent(button, 'click'); | ||
expect(this.el.innerHTML).toMatch(/<iframe(?: width="854"| height="480"| src="https:\/\/www.youtube.com\/embed\/QHH3iSeDBLo"| frameborder="0"| allowfullscreen=""){5}>(<br>)?<\/iframe>/gi); | ||
}); | ||
}); | ||
describe('OrderedList', function () { | ||
@@ -749,0 +769,0 @@ it('button should be active if the selection already has the element', function () { |
@@ -607,2 +607,7 @@ (function () { | ||
if (action === 'html') { | ||
var html = this.options.contentWindow.getSelection().toString().trim(); | ||
return MediumEditor.util.insertHTMLCommand(this.options.ownerDocument, html); | ||
} | ||
/* Issue: https://github.com/yabwe/medium-editor/issues/595 | ||
@@ -609,0 +614,0 @@ * If the action is to justify the text */ |
@@ -90,2 +90,10 @@ (function () { | ||
}, | ||
'html': { | ||
name: 'html', | ||
action: 'html', | ||
aria: 'evaluate html', | ||
tagNames: ['iframe', 'object'], | ||
contentDefault: '<b>html</b>', | ||
contentFA: '<i class="fa fa-code"></i>' | ||
}, | ||
'orderedlist': { | ||
@@ -251,2 +259,2 @@ name: 'orderedlist', | ||
})(); | ||
})(); |
@@ -260,18 +260,31 @@ (function () { | ||
var urlSchemeRegex = /^([a-z]+:)?\/\/|^(mailto|tel|maps):|^\#/i, | ||
hasScheme = urlSchemeRegex.test(value), | ||
scheme = '', | ||
// telRegex is a regex for checking if the string is a telephone number | ||
telRegex = /^\+?\s?\(?(?:\d\s?\-?\)?){3,20}$/, | ||
split = value.split('?'), | ||
path = split[0], | ||
query = split[1]; | ||
urlParts = value.match(/^(.*?)(?:\?(.*?))?(?:#(.*))?$/), | ||
path = urlParts[1], | ||
query = urlParts[2], | ||
fragment = urlParts[3]; | ||
if (telRegex.test(value)) { | ||
return 'tel:' + value; | ||
} else { | ||
// Check for URL scheme and default to http:// if none found | ||
return (urlSchemeRegex.test(value) ? '' : 'http://') + | ||
// Ensure path is encoded | ||
this.ensureEncodedUri(path) + | ||
// Ensure query is encoded | ||
(query === undefined ? '' : '?' + this.ensureEncodedQuery(query)); | ||
} | ||
if (!hasScheme) { | ||
var host = path.split('/')[0]; | ||
// if the host part of the path looks like a hostname | ||
if (host.match(/.+(\.|:).+/) || host === 'localhost') { | ||
scheme = 'http://'; | ||
} | ||
} | ||
return scheme + | ||
// Ensure path is encoded | ||
this.ensureEncodedUri(path) + | ||
// Ensure query is encoded | ||
(query === undefined ? '' : '?' + this.ensureEncodedQuery(query)) + | ||
// Include fragment unencoded as encodeUriComponent is too | ||
// heavy handed for the many characters allowed in a fragment | ||
(fragment === undefined ? '' : '#' + fragment); | ||
}, | ||
@@ -278,0 +291,0 @@ |
@@ -121,2 +121,3 @@ # Extensions | ||
* removeFormat | ||
* html | ||
@@ -123,0 +124,0 @@ #### Examples of custom built external buttons: |
@@ -616,2 +616,7 @@ /*global NodeFilter*/ | ||
/* | ||
* this function adds one or several classes on an a element. | ||
* if el parameter is not an a, it will look for a children of el. | ||
* if no a children are found, it will look for the a parent. | ||
*/ | ||
addClassToAnchors: function (el, buttonClass) { | ||
@@ -626,3 +631,9 @@ var classes = buttonClass.split(' '), | ||
} else { | ||
el = el.getElementsByTagName('a'); | ||
var aChildren = el.getElementsByTagName('a'); | ||
if (aChildren.length === 0) { | ||
var parentAnchor = Util.getClosestTag(el, 'a'); | ||
el = parentAnchor ? [parentAnchor] : []; | ||
} else { | ||
el = aChildren; | ||
} | ||
for (i = 0; i < el.length; i += 1) { | ||
@@ -629,0 +640,0 @@ for (j = 0; j < classes.length; j += 1) { |
@@ -18,3 +18,3 @@ MediumEditor.parseVersionString = function (release) { | ||
// grunt-bump looks for this: | ||
'version': '5.22.2' | ||
'version': '5.23.0' | ||
}).version); |
Sorry, the diff of this file is not supported yet
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1956712
138
25281
726
25