From 4a25f6acd84888f02176bac991e727568b7f9c31 Mon Sep 17 00:00:00 2001 From: Morgan Croney Date: Tue, 20 Sep 2016 12:37:13 -0400 Subject: [PATCH] support encoded urls --- spec/anchor.spec.js | 86 +++++++++++++++++++++++++++++++++++-- src/js/extensions/anchor.js | 34 +++++++++++++-- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/spec/anchor.spec.js b/spec/anchor.spec.js index 7912db213..483caa7c2 100644 --- a/spec/anchor.spec.js +++ b/spec/anchor.spec.js @@ -358,10 +358,10 @@ describe('Anchor Button TestCase', function () { it('should change spaces to %20 for a valid url if linkValidation options is set to true', function () { var editor = this.newMediumEditor('.editor', { - anchor: { - linkValidation: true - } - }), + anchor: { + linkValidation: true + } + }), link, anchorExtension = editor.getExtensionByName('anchor'), expectedOpts = { @@ -382,6 +382,84 @@ describe('Anchor Button TestCase', function () { expect(link.href).toBe(expectedOpts.value); }); + it('should not encode an encoded URL if linkValidation options is set to true', function () { + var editor = this.newMediumEditor('.editor', { + anchor: { + linkValidation: true + } + }), + link, + anchorExtension = editor.getExtensionByName('anchor'), + expectedOpts = { + value: 'http://a%20b.com/', + target: '_self' + }; + + spyOn(editor, 'execAction').and.callThrough(); + + selectElementContentsAndFire(editor.elements[0]); + anchorExtension.showForm('a%20b.com/'); + 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 encode query params if linkValidation options is set to true', function () { + var editor = this.newMediumEditor('.editor', { + anchor: { + linkValidation: true + } + }), + link, + anchorExtension = editor.getExtensionByName('anchor'), + expectedOpts = { + value: 'http://a.com/?q=http%3A%2F%2Fb.com&q2=http%3A%2F%2Fc.com', + target: '_self' + }; + + spyOn(editor, 'execAction').and.callThrough(); + + selectElementContentsAndFire(editor.elements[0]); + anchorExtension.showForm('a.com/?q=http://b.com&q2=http://c.com'); + 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 query param if linkValidation options is set to true', function () { + var editor = this.newMediumEditor('.editor', { + anchor: { + linkValidation: true + } + }), + link, + anchorExtension = editor.getExtensionByName('anchor'), + expectedOpts = { + value: 'http://a.com/?q=http%3A%2F%2Fb.com&q2=http%3A%2F%2Fc.com', + target: '_self' + }; + + spyOn(editor, 'execAction').and.callThrough(); + + selectElementContentsAndFire(editor.elements[0]); + anchorExtension.showForm('a.com/?q=http%3A%2F%2Fb.com&q2=http://c.com'); + 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 change spaces to %20 if linkValidation is set to false', function () { var editor = this.newMediumEditor('.editor', { anchor: { diff --git a/src/js/extensions/anchor.js b/src/js/extensions/anchor.js index 0b1a07df7..22bc26333 100644 --- a/src/js/extensions/anchor.js +++ b/src/js/extensions/anchor.js @@ -231,19 +231,47 @@ this.base.checkSelection(); }, + ensureEncodedUri: function (str) { + return str === decodeURI(str) ? encodeURI(str) : str; + }, + + ensureEncodedUriComponent: function (str) { + return str === decodeURIComponent(str) ? encodeURIComponent(str) : str; + }, + + ensureEncodedParam: function (param) { + var split = param.split('='), + key = split[0], + val = split[1]; + + return key + (val === undefined ? '' : '=' + this.ensureEncodedUriComponent(val)); + }, + + ensureEncodedQuery: function (queryString) { + return queryString.split('&').map(this.ensureEncodedParam.bind(this)).join('&'); + }, + checkLinkFormat: function (value) { // Matches any alphabetical characters followed by :// // Matches protocol relative "//" // Matches common external protocols "mailto:" "tel:" "maps:" // Matches relative hash link, begins with "#" var urlSchemeRegex = /^([a-z]+:)?\/\/|^(mailto|tel|maps):|^\#/i, - // var te is a regex for checking if the string is a telephone number - telRegex = /^\+?\s?\(?(?:\d\s?\-?\)?){3,20}$/; + // var te 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]; + if (telRegex.test(value)) { return 'tel:' + value; } else { // Check for URL scheme and default to http:// if none found - return (urlSchemeRegex.test(value) ? '' : 'http://') + encodeURI(value); + return (urlSchemeRegex.test(value) ? '' : 'http://') + + // Ensure path is encoded + this.ensureEncodedUri(path) + + // Ensure query is encoded + (query === undefined ? '' : '?' + this.ensureEncodedQuery(query)); } },