From 0e5978c96d4d700daa075df63b98069af17ba102 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Tue, 6 Oct 2015 21:52:06 -0700 Subject: [PATCH 01/14] Add the froala editor to the inplace text area component The editor component does not yet pass actions back correctly so we have to use query to get the value out before we save. --- app/components/big-text.js | 14 +++++-- app/components/inplace-textarea.js | 39 +++++++++++++++++++ app/templates/components/inplace-textarea.hbs | 10 +---- bower.json | 3 +- ember-cli-build.js | 4 +- package.json | 1 + 6 files changed, 58 insertions(+), 13 deletions(-) diff --git a/app/components/big-text.js b/app/components/big-text.js index 710a787595..6a13b6fd0b 100644 --- a/app/components/big-text.js +++ b/app/components/big-text.js @@ -16,12 +16,20 @@ export default Ember.Component.extend({ }.property('displayText', 'text'), cleanText: function(){ var text = this.get('text'); + //give us a string to work with no matter what if(text === undefined || text == null){ - return this.get('promptText')?this.get('promptText').toString():''; + text = ''; } + //strip any possible HTML out of the text - return text.replace(/(<([^>]+)>)/ig,""); - }.property('text'), + let cleanText = text.replace(/(<([^>]+)>)/ig,""); + + if(cleanText.length < 1){ + return this.get('promptText')?this.get('promptText').toString():''; + } + + return cleanText; + }.property('text', 'promptText'), displayText: function(){ var text = this.get('cleanText'); if(this.get('expanded') || text.length < this.get('totalLength')){ diff --git a/app/components/inplace-textarea.js b/app/components/inplace-textarea.js index 636e1f01c1..cae3edbfdc 100644 --- a/app/components/inplace-textarea.js +++ b/app/components/inplace-textarea.js @@ -3,4 +3,43 @@ import InPlace from 'ilios/mixins/inplace'; export default Ember.Component.extend(InPlace, { classNames: ['editinplace', 'inplace-textarea'], + editorParams: Ember.computed('buffer', function(){ + let params = { + inlineMode: false, + placeholder: '', + allowHTML: true, + autosave: false, + plainPaste: true, + spellcheck: true, + buttons: [ + 'bold', + 'italic', + 'underline', + 'fontSize', + 'color', + 'insertOrderedList', + 'insertUnorderedList', + 'createLink', + 'undo', + 'redo', + 'html', + 'removeFormat', + 'fullscreen' + ] + }; + + return params; + }), + willDestroyElement(){ + if (this.$('.control .froala-box').data('fa.editable')) { + this.$('.control .froala-box').editable('destroy'); + } + }, + actions: { + pullDataAndSave(){ + let value = this.$('.control .froala-box').editable('getHTML'); + this.send('changeValue', value); + this.send('save'); + } + } }); diff --git a/app/templates/components/inplace-textarea.hbs b/app/templates/components/inplace-textarea.hbs index 17865c3317..6d5239d893 100644 --- a/app/templates/components/inplace-textarea.hbs +++ b/app/templates/components/inplace-textarea.hbs @@ -6,16 +6,10 @@ {{else}} - + {{froala-editor params=editorParams value=buffer}} - + diff --git a/bower.json b/bower.json index fba591ac3f..e4cff9dfab 100644 --- a/bower.json +++ b/bower.json @@ -25,6 +25,7 @@ "font-awesome": "~4.4.0", "pikaday": "~1.3.3", "moment": ">= 2.8.0", - "moment-timezone": ">= 0.1.0" + "moment-timezone": ">= 0.1.0", + "FroalaWysiwygEditor": "~1.2.8" } } diff --git a/ember-cli-build.js b/ember-cli-build.js index a5a593e269..e5ff9f03fe 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -33,6 +33,8 @@ module.exports = function(defaults) { useScss: true }, }); - + app.import('bower_components/FroalaWysiwygEditor/js/plugins/lists.min.js'); + app.import('bower_components/FroalaWysiwygEditor/js/plugins/fullscreen.min.js'); + return app.toTree(); }; diff --git a/package.json b/package.json index a3490ac533..bec1002acd 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "ember-deploy-s3": "0.0.5", "ember-deploy-s3-index": "^0.4.0", "ember-disable-proxy-controllers": "^1.0.0", + "ember-froala": "1.1.1", "ember-i18n": "4.1.3", "ember-legacy-views": "0.2.0", "ember-load": "0.0.2", From 86bfde60754b91390f20ff988ca2c8d2a3a0ffca Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 14 Oct 2015 22:33:39 -0700 Subject: [PATCH 02/14] Adjustments to the experimental froala editor New version of ember-froala allows for better binding of actions. --- app/components/big-text.js | 55 ++++++++++++------- app/components/inplace-textarea.js | 24 +++----- app/templates/components/inplace-textarea.hbs | 8 ++- package.json | 2 +- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/app/components/big-text.js b/app/components/big-text.js index 6a13b6fd0b..a7601dbe18 100644 --- a/app/components/big-text.js +++ b/app/components/big-text.js @@ -1,5 +1,9 @@ import Ember from 'ember'; +const {computed, Handlebars} = Ember; +const {SafeString} = Handlebars; +const {collect, sum} = computed; + export default Ember.Component.extend({ expanded: false, classNames: ['big-text'], @@ -8,36 +12,47 @@ export default Ember.Component.extend({ expandIcon: 'info-circle', text: '', ellipsis: 'ellipsis-h', - lengths: Ember.computed.collect('length', 'slippage'), - totalLength: Ember.computed.sum('lengths'), + lengths: collect('length', 'slippage'), + totalLength: sum('lengths'), promptText: '', - showIcons: function(){ + renderHtml: true, + showIcons: computed('displayText', 'text', function(){ + return false; return this.get('displayText') !== this.get('cleanText'); - }.property('displayText', 'text'), - cleanText: function(){ - var text = this.get('text'); + }), + textOrPrompt: computed('text', 'promptText', function(){ + let text = this.get('text'); //give us a string to work with no matter what if(text === undefined || text == null){ text = ''; } - //strip any possible HTML out of the text - let cleanText = text.replace(/(<([^>]+)>)/ig,""); - - if(cleanText.length < 1){ - return this.get('promptText')?this.get('promptText').toString():''; + if(text.length < 1 && this.get('promptText')){ + text = this.get('promptText').toString(); } - return cleanText; - }.property('text', 'promptText'), - displayText: function(){ - var text = this.get('cleanText'); - if(this.get('expanded') || text.length < this.get('totalLength')){ - return text; + return text; + }), + cleanText: computed('textOrPrompt', function(){ + //strip any possible HTML out of the text + return this.get('textOrPrompt').replace(/(<([^>]+)>)/ig,""); + }), + displayText: computed('cleanText', 'totalLength', 'length', 'expanded', function(){ + let cleanText = this.get('cleanText'); + let text; + if(this.get('expanded') || cleanText.length < this.get('totalLength')){ + if(this.get('renderHtml')){ + text = this.get('textOrPrompt'); + } else { + text = cleanText; + } + } else { + text = cleanText.substring(0, this.get('length')); } - - return text.substring(0, this.get('length')); - }.property('cleanText', 'totalLength', 'length', 'expanded'), + + return new SafeString(text); + + }), actions: { click: function(){ this.sendAction(); diff --git a/app/components/inplace-textarea.js b/app/components/inplace-textarea.js index cae3edbfdc..e9e851816a 100644 --- a/app/components/inplace-textarea.js +++ b/app/components/inplace-textarea.js @@ -3,7 +3,7 @@ import InPlace from 'ilios/mixins/inplace'; export default Ember.Component.extend(InPlace, { classNames: ['editinplace', 'inplace-textarea'], - editorParams: Ember.computed('buffer', function(){ + editorParams: Ember.computed('buffer', 'clickPrompt', function(){ let params = { inlineMode: false, placeholder: '', @@ -15,31 +15,25 @@ export default Ember.Component.extend(InPlace, { 'bold', 'italic', 'underline', - 'fontSize', - 'color', + 'subscript', + 'superscript', 'insertOrderedList', 'insertUnorderedList', 'createLink', - 'undo', - 'redo', - 'html', - 'removeFormat', - 'fullscreen' + 'html' ] }; return params; }), willDestroyElement(){ - if (this.$('.control .froala-box').data('fa.editable')) { - this.$('.control .froala-box').editable('destroy'); - } + this.$('.control .froala-box').editable('destroy'); }, actions: { - pullDataAndSave(){ - let value = this.$('.control .froala-box').editable('getHTML'); - this.send('changeValue', value); - this.send('save'); + grabChangedValue: function(event, editor){ + if(editor){ + this.send('changeValue', editor.getHTML()); + } } } }); diff --git a/app/templates/components/inplace-textarea.hbs b/app/templates/components/inplace-textarea.hbs index 6d5239d893..da5d430cae 100644 --- a/app/templates/components/inplace-textarea.hbs +++ b/app/templates/components/inplace-textarea.hbs @@ -6,10 +6,14 @@ {{else}} - {{froala-editor params=editorParams value=buffer}} + {{froala-editor + params=editorParams + value=buffer + contentChanged=(action "grabChangedValue") + }} - + diff --git a/package.json b/package.json index bec1002acd..e4c4914083 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "ember-deploy-s3": "0.0.5", "ember-deploy-s3-index": "^0.4.0", "ember-disable-proxy-controllers": "^1.0.0", - "ember-froala": "1.1.1", + "ember-froala": "1.2.0", "ember-i18n": "4.1.3", "ember-legacy-views": "0.2.0", "ember-load": "0.0.2", From bdd537d619c48158b68ea1ca6f802f21f2a9daca Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Thu, 15 Oct 2015 15:16:34 -0700 Subject: [PATCH 03/14] Add inplace-html to handle WYSIWYG editing --- app/components/inplace-html.js | 39 +++++++++++++++++++ app/templates/components/.gitkeep | 0 app/templates/components/inplace-html.hbs | 21 ++++++++++ ember-cli-build.js | 1 - .../components/inplace-html-test.js | 26 +++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 app/components/inplace-html.js delete mode 100644 app/templates/components/.gitkeep create mode 100644 app/templates/components/inplace-html.hbs create mode 100644 tests/integration/components/inplace-html-test.js diff --git a/app/components/inplace-html.js b/app/components/inplace-html.js new file mode 100644 index 0000000000..61f945395b --- /dev/null +++ b/app/components/inplace-html.js @@ -0,0 +1,39 @@ +import Ember from 'ember'; +import InPlace from 'ilios/mixins/inplace'; + +export default Ember.Component.extend(InPlace, { + classNames: ['editinplace', 'inplace-html'], + editorParams: Ember.computed('buffer', 'clickPrompt', function(){ + let params = { + inlineMode: false, + placeholder: '', + allowHTML: true, + autosave: false, + plainPaste: true, + spellcheck: true, + buttons: [ + 'bold', + 'italic', + 'underline', + 'subscript', + 'superscript', + 'insertOrderedList', + 'insertUnorderedList', + 'createLink', + // 'html' temporarily disabled due to bug + ] + }; + + return params; + }), + willDestroyElement(){ + this.$('.control .froala-box').editable('destroy'); + }, + actions: { + grabChangedValue: function(event, editor){ + if(editor){ + this.send('changeValue', editor.getHTML()); + } + } + } +}); diff --git a/app/templates/components/.gitkeep b/app/templates/components/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app/templates/components/inplace-html.hbs b/app/templates/components/inplace-html.hbs new file mode 100644 index 0000000000..da5d430cae --- /dev/null +++ b/app/templates/components/inplace-html.hbs @@ -0,0 +1,21 @@ + + {{#unless isEditing}} + + {{big-text text=value action='edit' promptText=clickPrompt}} + + {{else}} + + + {{froala-editor + params=editorParams + value=buffer + contentChanged=(action "grabChangedValue") + }} + + + + + + + {{/unless}} + diff --git a/ember-cli-build.js b/ember-cli-build.js index e5ff9f03fe..528014e1d4 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -34,7 +34,6 @@ module.exports = function(defaults) { }, }); app.import('bower_components/FroalaWysiwygEditor/js/plugins/lists.min.js'); - app.import('bower_components/FroalaWysiwygEditor/js/plugins/fullscreen.min.js'); return app.toTree(); }; diff --git a/tests/integration/components/inplace-html-test.js b/tests/integration/components/inplace-html-test.js new file mode 100644 index 0000000000..47435efbb2 --- /dev/null +++ b/tests/integration/components/inplace-html-test.js @@ -0,0 +1,26 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('inplace-html', 'Integration | Component | inplace html', { + integration: true +}); + +test('it renders', function(assert) { + assert.expect(2); + + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... }); + + this.render(hbs`{{inplace-html}}`); + + assert.equal(this.$().text().trim(), ''); + + // Template block usage: + this.render(hbs` + {{#inplace-html}} + template block text + {{/inplace-html}} + `); + + assert.equal(this.$().text().trim(), 'template block text'); +}); From 0a50aa10a5c30b6355d56412d1629ed8965af6d4 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Thu, 15 Oct 2015 15:18:41 -0700 Subject: [PATCH 04/14] Revert inplace-textarea to its original state --- app/components/inplace-textarea.js | 33 ------------------- app/templates/components/inplace-textarea.hbs | 12 ++++--- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/app/components/inplace-textarea.js b/app/components/inplace-textarea.js index e9e851816a..636e1f01c1 100644 --- a/app/components/inplace-textarea.js +++ b/app/components/inplace-textarea.js @@ -3,37 +3,4 @@ import InPlace from 'ilios/mixins/inplace'; export default Ember.Component.extend(InPlace, { classNames: ['editinplace', 'inplace-textarea'], - editorParams: Ember.computed('buffer', 'clickPrompt', function(){ - let params = { - inlineMode: false, - placeholder: '', - allowHTML: true, - autosave: false, - plainPaste: true, - spellcheck: true, - buttons: [ - 'bold', - 'italic', - 'underline', - 'subscript', - 'superscript', - 'insertOrderedList', - 'insertUnorderedList', - 'createLink', - 'html' - ] - }; - - return params; - }), - willDestroyElement(){ - this.$('.control .froala-box').editable('destroy'); - }, - actions: { - grabChangedValue: function(event, editor){ - if(editor){ - this.send('changeValue', editor.getHTML()); - } - } - } }); diff --git a/app/templates/components/inplace-textarea.hbs b/app/templates/components/inplace-textarea.hbs index da5d430cae..17865c3317 100644 --- a/app/templates/components/inplace-textarea.hbs +++ b/app/templates/components/inplace-textarea.hbs @@ -6,11 +6,13 @@ {{else}} - {{froala-editor - params=editorParams - value=buffer - contentChanged=(action "grabChangedValue") - }} + From 3ee7d3a20526404e9ffee5ecb7cbb7e89a88c49d Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Thu, 15 Oct 2015 15:23:55 -0700 Subject: [PATCH 05/14] Remove the promptText concept from big-text component --- app/components/big-text.js | 27 ++++++------------- app/templates/components/inplace-html.hbs | 6 ++++- app/templates/components/inplace-textarea.hbs | 8 ++++-- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/app/components/big-text.js b/app/components/big-text.js index a7601dbe18..b74921800d 100644 --- a/app/components/big-text.js +++ b/app/components/big-text.js @@ -14,35 +14,24 @@ export default Ember.Component.extend({ ellipsis: 'ellipsis-h', lengths: collect('length', 'slippage'), totalLength: sum('lengths'), - promptText: '', renderHtml: true, - showIcons: computed('displayText', 'text', function(){ - return false; - return this.get('displayText') !== this.get('cleanText'); - }), - textOrPrompt: computed('text', 'promptText', function(){ - let text = this.get('text'); - //give us a string to work with no matter what - if(text === undefined || text == null){ - text = ''; - } - - if(text.length < 1 && this.get('promptText')){ - text = this.get('promptText').toString(); + showIcons: computed('displayText', 'text', 'renderHtml', function(){ + if(this.get('renderHtml')){ + return this.get('displayText') !== this.get('text'); + } else { + return this.get('displayText') !== this.get('cleanText'); } - - return text; }), - cleanText: computed('textOrPrompt', function(){ + cleanText: computed('text', function(){ //strip any possible HTML out of the text - return this.get('textOrPrompt').replace(/(<([^>]+)>)/ig,""); + return this.get('text').replace(/(<([^>]+)>)/ig,""); }), displayText: computed('cleanText', 'totalLength', 'length', 'expanded', function(){ let cleanText = this.get('cleanText'); let text; if(this.get('expanded') || cleanText.length < this.get('totalLength')){ if(this.get('renderHtml')){ - text = this.get('textOrPrompt'); + text = this.get('text'); } else { text = cleanText; } diff --git a/app/templates/components/inplace-html.hbs b/app/templates/components/inplace-html.hbs index da5d430cae..b2f62188ce 100644 --- a/app/templates/components/inplace-html.hbs +++ b/app/templates/components/inplace-html.hbs @@ -1,7 +1,11 @@ {{#unless isEditing}} - {{big-text text=value action='edit' promptText=clickPrompt}} + {{#if value.length}} + {{big-text text=value action='edit' renderHtml=true}} + {{else}} + {{clickPrompt}} + {{/if}} {{else}} diff --git a/app/templates/components/inplace-textarea.hbs b/app/templates/components/inplace-textarea.hbs index 17865c3317..79e2fea650 100644 --- a/app/templates/components/inplace-textarea.hbs +++ b/app/templates/components/inplace-textarea.hbs @@ -1,14 +1,18 @@ {{#unless isEditing}} - {{big-text text=value action='edit' promptText=clickPrompt}} + {{#if value.length}} + {{big-text text=value action='edit'}} + {{else}} + {{clickPrompt}} + {{/if}} {{else}}