From 7954200d0c5ccc64531d251fc86b4eca8a964c80 Mon Sep 17 00:00:00 2001 From: Filip Date: Wed, 8 Mar 2017 12:08:31 +0100 Subject: [PATCH 1/5] Fixed support for undo/redo/cut/paste events --- dist/tinymce.min.js | 2 +- src/tinymce.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dist/tinymce.min.js b/dist/tinymce.min.js index cd6b161..78c6d9b 100644 --- a/dist/tinymce.min.js +++ b/dist/tinymce.min.js @@ -1 +1 @@ -angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r={debounce:!0},s=function(b){var c=b.getContent({format:r.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},t=g.getUniqueId();j.$set("id",t),n={},angular.extend(n,h.$eval(j.uiTinymce));var u=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){a.isDirty()&&(a.save(),s(a))}(d)},a)}}(400),v={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine()}),b.on("ExecCommand change NodeChange ObjectResized",function(){return r.debounce?void u(b):(b.save(),void s(b))}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:s}),n.setup&&n.setup(b,{updateView:s})},format:n.format||"html",selector:"#"+j.id};angular.extend(r,f,n,v),c(function(){r.baseURL&&(tinymce.baseURL=r.baseURL);var a=tinymce.init(r);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file +angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r=!1,s={debounce:!0},t=function(b){var c=b.getContent({format:s.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},u=g.getUniqueId();j.$set("id",u),n={},angular.extend(n,h.$eval(j.uiTinymce));var v=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){(a.isDirty()||r)&&(a.save(),t(a))}(d)},a)}}(400),w={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine()}),b.on("ExecCommand change NodeChange ObjectResized undo redo cut paste",function(a){return["undo","redo","cut","paste"].indexOf(a.type)!==-1&&(r=!0),s.debounce?void v(b):(b.save(),void t(b))}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:t}),n.setup&&n.setup(b,{updateView:t})},format:n.format||"html",selector:"#"+j.id};angular.extend(s,f,n,w),c(function(){s.baseURL&&(tinymce.baseURL=s.baseURL);var a=tinymce.init(s);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file diff --git a/src/tinymce.js b/src/tinymce.js index c0291af..f402c42 100644 --- a/src/tinymce.js +++ b/src/tinymce.js @@ -21,6 +21,8 @@ angular.module('ui.tinymce', []) var ngModel = ctrls[0], form = ctrls[1] || null; + var forceDirty = false; + var expression, options = { debounce: true }, tinyInstance, @@ -65,7 +67,7 @@ angular.module('ui.tinymce', []) $timeout.cancel(debouncedUpdateTimer); debouncedUpdateTimer = $timeout(function() { return (function(ed) { - if (ed.isDirty()) { + if (ed.isDirty() || forceDirty) { ed.save(); updateView(ed); } @@ -92,7 +94,11 @@ angular.module('ui.tinymce', []) // - the editor content has been modified [change] // - the node has changed [NodeChange] // - an object has been resized (table, image) [ObjectResized] - ed.on('ExecCommand change NodeChange ObjectResized', function() { + // - undo, redo, cut or paste operations were performed [undo, redo, cut, paste] + ed.on('ExecCommand change NodeChange ObjectResized undo redo cut paste', function(e) { + if (['undo', 'redo', 'cut', 'paste'].indexOf(e.type) !== -1) { + forceDirty = true; + } if (!options.debounce) { ed.save(); updateView(ed); From 02734579ea716511027dc2732765337170cc42ce Mon Sep 17 00:00:00 2001 From: Filip Date: Thu, 9 Mar 2017 14:28:25 +0100 Subject: [PATCH 2/5] Add keyDown listener to properly catch select all + delete --- dist/tinymce.min.js | 2 +- src/tinymce.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dist/tinymce.min.js b/dist/tinymce.min.js index 78c6d9b..db35f75 100644 --- a/dist/tinymce.min.js +++ b/dist/tinymce.min.js @@ -1 +1 @@ -angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r=!1,s={debounce:!0},t=function(b){var c=b.getContent({format:s.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},u=g.getUniqueId();j.$set("id",u),n={},angular.extend(n,h.$eval(j.uiTinymce));var v=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){(a.isDirty()||r)&&(a.save(),t(a))}(d)},a)}}(400),w={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine()}),b.on("ExecCommand change NodeChange ObjectResized undo redo cut paste",function(a){return["undo","redo","cut","paste"].indexOf(a.type)!==-1&&(r=!0),s.debounce?void v(b):(b.save(),void t(b))}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:t}),n.setup&&n.setup(b,{updateView:t})},format:n.format||"html",selector:"#"+j.id};angular.extend(s,f,n,w),c(function(){s.baseURL&&(tinymce.baseURL=s.baseURL);var a=tinymce.init(s);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file +angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r=!1,s={debounce:!0},t=function(b){var c=b.getContent({format:s.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},u=g.getUniqueId();j.$set("id",u),n={},angular.extend(n,h.$eval(j.uiTinymce));var v=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){(a.isDirty()||r)&&(a.save(),t(a))}(d)},a)}}(400),w={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine()}),b.on("ExecCommand change NodeChange ObjectResized undo redo cut paste",function(a){return["undo","redo","cut","paste"].indexOf(a.type)!==-1&&(r=!0),s.debounce?void v(b):(b.save(),void t(b))}),b.on("keyDown",function(){r=!0}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:t}),n.setup&&n.setup(b,{updateView:t})},format:n.format||"html",selector:"#"+j.id};angular.extend(s,f,n,w),c(function(){s.baseURL&&(tinymce.baseURL=s.baseURL);var a=tinymce.init(s);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file diff --git a/src/tinymce.js b/src/tinymce.js index f402c42..feba8ab 100644 --- a/src/tinymce.js +++ b/src/tinymce.js @@ -97,7 +97,7 @@ angular.module('ui.tinymce', []) // - undo, redo, cut or paste operations were performed [undo, redo, cut, paste] ed.on('ExecCommand change NodeChange ObjectResized undo redo cut paste', function(e) { if (['undo', 'redo', 'cut', 'paste'].indexOf(e.type) !== -1) { - forceDirty = true; + forceDirty = true; } if (!options.debounce) { ed.save(); @@ -107,6 +107,10 @@ angular.module('ui.tinymce', []) debouncedUpdate(ed); }); + ed.on('keyDown', function() { + forceDirty = true; + }); + ed.on('blur', function() { element[0].blur(); ngModel.$setTouched(); From c5ae8a45deef7db23a88aa1fa64ecf6b762c86b1 Mon Sep 17 00:00:00 2001 From: Michal Filip Date: Wed, 24 May 2017 16:07:25 +0200 Subject: [PATCH 3/5] Added accessibility fallback for older browsers - hotkeys instructions --- dist/tinymce.min.js | 2 +- src/tinymce.js | 460 +++++++++++++++++++++++--------------------- 2 files changed, 237 insertions(+), 225 deletions(-) diff --git a/dist/tinymce.min.js b/dist/tinymce.min.js index db35f75..f6c74cf 100644 --- a/dist/tinymce.min.js +++ b/dist/tinymce.min.js @@ -1 +1 @@ -angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r=!1,s={debounce:!0},t=function(b){var c=b.getContent({format:s.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},u=g.getUniqueId();j.$set("id",u),n={},angular.extend(n,h.$eval(j.uiTinymce));var v=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){(a.isDirty()||r)&&(a.save(),t(a))}(d)},a)}}(400),w={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine()}),b.on("ExecCommand change NodeChange ObjectResized undo redo cut paste",function(a){return["undo","redo","cut","paste"].indexOf(a.type)!==-1&&(r=!0),s.debounce?void v(b):(b.save(),void t(b))}),b.on("keyDown",function(){r=!0}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:t}),n.setup&&n.setup(b,{updateView:t})},format:n.format||"html",selector:"#"+j.id};angular.extend(s,f,n,w),c(function(){s.baseURL&&(tinymce.baseURL=s.baseURL);var a=tinymce.init(s);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file +angular.module("ui.tinymce",[]).value("uiTinymceConfig",{}).directive("uiTinymce",["$rootScope","$compile","$timeout","$window","$sce","uiTinymceConfig","uiTinymceService",function(a,b,c,d,e,f,g){return f=f||{},f.baseUrl&&(tinymce.baseURL=f.baseUrl),{require:["ngModel","^?form"],priority:599,link:function(h,i,j,k){function l(a){a?(m(),o&&o.getBody().setAttribute("contenteditable",!1)):(m(),o&&!o.settings.readonly&&o.getDoc()&&o.getBody().setAttribute("contenteditable",!0))}function m(){o||(o=tinymce.get(j.id))}if(d.tinymce){var n,o,p=k[0],q=k[1]||null,r=!1,s={debounce:!0},t=function(b){var c=b.getContent({format:s.format}).trim();c=e.trustAsHtml(c),p.$setViewValue(c),a.$$phase||h.$digest()},u=g.getUniqueId();j.$set("id",u),n={},angular.extend(n,h.$eval(j.uiTinymce));var v=function(a){var b;return function(d){c.cancel(b),b=c(function(){return function(a){(a.isDirty()||r)&&(a.save(),t(a))}(d)},a)}}(400),w={setup:function(b){b.on("init",function(){p.$render(),p.$setPristine(),p.$setUntouched(),q&&q.$setPristine();var a=this.editorManager.activeEditor,b=tinymce.DOM.get(a.id+"_ifr"),c=tinymce.DOM.getAttrib(a.id,"aria-labelledby");if(document.getElementById(c)){var d=document.getElementById(c).innerText;d&&(d+=" Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help",a.dom.setAttrib(b,"title",d))}}),b.on("ExecCommand change NodeChange ObjectResized undo redo cut paste",function(a){return["undo","redo","cut","paste"].indexOf(a.type)!==-1&&(r=!0),s.debounce?void v(b):(b.save(),void t(b))}),b.on("keyDown",function(){r=!0}),b.on("blur",function(){i[0].blur(),p.$setTouched(),a.$$phase||h.$digest()}),b.on("remove",function(){i.remove()}),f.setup&&f.setup(b,{updateView:t}),n.setup&&n.setup(b,{updateView:t})},format:n.format||"html",selector:"#"+j.id};angular.extend(s,f,n,w),c(function(){s.baseURL&&(tinymce.baseURL=s.baseURL);var a=tinymce.init(s);a&&"function"==typeof a.then?a.then(function(){l(h.$eval(j.ngDisabled))}):l(h.$eval(j.ngDisabled))}),p.$formatters.unshift(function(a){return a?e.trustAsHtml(a):""}),p.$parsers.unshift(function(a){return a?e.getTrustedHtml(a):""}),p.$render=function(){m();var a=p.$viewValue?e.getTrustedHtml(p.$viewValue):"";o&&o.getDoc()&&(o.setContent(a),o.fire("change"))},j.$observe("disabled",l),h.$on("$tinymce:refresh",function(a,c){var d=j.id;if(angular.isUndefined(c)||c===d){var e=i.parent(),f=i.clone();f.removeAttr("id"),f.removeAttr("style"),f.removeAttr("aria-hidden"),tinymce.execCommand("mceRemoveEditor",!1,d),e.append(b(f)(h))}}),h.$on("$destroy",function(){m(),o&&(o.remove(),o=null)})}}}}]).service("uiTinymceService",[function(){var a=function(){var a="ui-tinymce",b=0,c=function(){return b++,a+"-"+b};return{getUniqueId:c}};return new a}]); \ No newline at end of file diff --git a/src/tinymce.js b/src/tinymce.js index feba8ab..2cd83bd 100644 --- a/src/tinymce.js +++ b/src/tinymce.js @@ -2,243 +2,255 @@ * Binds a TinyMCE widget to