<%= yield %>
diff --git a/lib/assets/.eslintrc.json b/lib/assets/.eslintrc.json
index 95727a0..b2243c8 100644
--- a/lib/assets/.eslintrc.json
+++ b/lib/assets/.eslintrc.json
@@ -1,7 +1,7 @@
{
"extends": "airbnb-base",
"env": {
- "jquery": true,
- "jasmine": true
+ "jasmine": true,
+ "jquery": true
}
}
diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js
index 6011eeb..262a25d 100644
--- a/lib/assets/javascripts/application.js
+++ b/lib/assets/javascripts/application.js
@@ -1,2 +1,3 @@
import './views/devise/devise';
import './views/contacts/new';
+import './views/plans/edit_details';
diff --git a/lib/assets/javascripts/utils/tinymce.js b/lib/assets/javascripts/utils/tinymce.js
new file mode 100644
index 0000000..d0b7e37
--- /dev/null
+++ b/lib/assets/javascripts/utils/tinymce.js
@@ -0,0 +1,108 @@
+// Import TinyMCE
+import tinymce from 'tinymce/tinymce';
+// Import TinyMCE theme
+import 'tinymce/themes/modern/theme';
+// Plugins
+import 'tinymce/plugins/table';
+import 'tinymce/plugins/autoresize';
+import 'tinymce/plugins/link';
+import 'tinymce/plugins/paste';
+import 'tinymce/plugins/advlist';
+// Other dependencies
+import { isObject, isString } from './isType';
+
+// Configuration extracted from https://www.tinymce.com/docs/advanced/usage-with-module-loaders/
+require.context(
+ 'file-loader?name=./javascripts/[path][name].[ext]&context=node_modules/tinymce!tinymce/skins',
+ true,
+ /.*/,
+);
+
+export const defaultOptions = {
+ selector: '.tinymce',
+ statusbar: false,
+ menubar: false,
+ toolbar: 'bold italic | bullist numlist | link | table',
+ plugins: 'table autoresize link paste advlist',
+ advlist_bullet_styles: 'circle,disc,square', // Only disc bullets display on htmltoword
+ target_list: false,
+ autoresize_min_height: 130,
+ autoresize_bottom_margin: 10,
+ extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]',
+ paste_auto_cleanup_on_paste: true,
+ paste_remove_styles: true,
+ paste_retain_style_properties: 'none',
+ paste_convert_middot_lists: true,
+ paste_remove_styles_if_webkit: true,
+ paste_remove_spans: true,
+ paste_strip_class_attributes: 'all',
+ table_default_attributes: {
+ border: 1,
+ },
+};
+
+export const Tinymce = {
+ /*
+ Initialises a tinymce editor given the object passed. If a non-valid object is passed,
+ the defaultOptions object is used instead
+ @param options - An object with tinyMCE properties
+ */
+ init(options = {}) {
+ if (isObject(options)) {
+ tinymce.init($.extend(true, defaultOptions, options));
+ } else {
+ tinymce.init(defaultOptions);
+ }
+ },
+ /*
+ Finds any tinyMCE editor whose target element/textarea has the className passed
+ @param className - A string representing the class name of the tinyMCE editor
+ target element/textarea to look for
+ @return An Array of tinymce.Editor objects
+ */
+ findEditorsByClassName(className) {
+ if (isString(className)) {
+ return tinymce.editors.reduce((acc, e) => {
+ if ($(e.getElement()).hasClass(className)) {
+ return acc.concat([e]);
+ }
+ return acc;
+ }, []);
+ }
+ return [];
+ },
+ /*
+ Finds a tinyMCE editor whose target element/textarea has the id passed
+ @param id - A string representing the id of the tinyMCE editor target
+ element/textarea to look for
+ @return tinymce.Editor object, otherwise undefined
+ */
+ findEditorById(id) {
+ if (isString(id)) {
+ return tinymce.editors.find(el => el.id === id);
+ }
+ return undefined;
+ },
+ /*
+ Destroy every editor instance whose target element/textarea has the className passed. This
+ method executes for each editor the method defined at tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy).
+ @param className - A string representing the class name of the tinyMCE editor
+ target element/textarea to look for
+ @return undefined
+ */
+ destroyEditorsByClassName(className) {
+ const editors = this.findEditorsByClassName(className);
+ editors.forEach(ed => ed.destroy(false));
+ },
+ /*
+ Destroy an editor instance whose target element/textarea has HTML id passed. This method
+ executes tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy) for a successfull id found.
+ @return undefined
+ */
+ destroyEditorById(id) {
+ const editor = this.findEditorById(id);
+ if (editor) {
+ editor.destroy(false);
+ }
+ },
+};
diff --git a/lib/assets/javascripts/utils/tinymceSpec.js b/lib/assets/javascripts/utils/tinymceSpec.js
new file mode 100644
index 0000000..2c6439f
--- /dev/null
+++ b/lib/assets/javascripts/utils/tinymceSpec.js
@@ -0,0 +1,50 @@
+import { Tinymce } from './tinymce';
+
+beforeEach(() => {
+ $('body').append('');
+ $('body').append('');
+ Tinymce.init({ selector: '.test' });
+});
+
+describe('findEditorsByClassName test suite', () => {
+ it('expect two editors with class name test', () => expect(Tinymce.findEditorsByClassName('test').length).toBe(2));
+ it('expect zero editors with class name whatever', () => expect(Tinymce.findEditorsByClassName('whatever').length).toBe(0));
+});
+
+describe('findEditorById test suite', () => {
+ it('expect editor with id test1 to be defined', () => expect(Tinymce.findEditorById('test1')).toBeDefined());
+ it('expect editor with id test2 to be defined', () => expect(Tinymce.findEditorById('test2')).toBeDefined());
+ it('expect editor with id whatever to be undefined', () => expect(Tinymce.findEditorById('whatever')).not.toBeDefined());
+});
+
+describe('destroyEditorsByClassName test suite', () => {
+ it('expect remaining two editors', () => {
+ Tinymce.destroyEditorsByClassName('whatever');
+ expect(Tinymce.findEditorsByClassName('test').length).toBe(2);
+ });
+ it('expect remaining zero editors', () => {
+ Tinymce.destroyEditorsByClassName('test');
+ expect(Tinymce.findEditorsByClassName('test').length).toBe(0);
+ });
+});
+
+describe('destroyEditorsById test suite', () => {
+ it('expect remaining two editors', () => {
+ Tinymce.destroyEditorById('test3');
+ expect(Tinymce.findEditorsByClassName('test').length).toBe(2);
+ });
+ it('expect remaining one editor', () => {
+ Tinymce.destroyEditorById('test1');
+ expect(Tinymce.findEditorsByClassName('test').length).toBe(1);
+ });
+ it('expect remaining zero editors', () => {
+ Tinymce.destroyEditorById('test1');
+ Tinymce.destroyEditorById('test2');
+ expect(Tinymce.findEditorsByClassName('test').length).toBe(0);
+ });
+});
+
+afterEach(() => {
+ $('body').html('');
+});
+
diff --git a/lib/assets/javascripts/views/plans/edit_details.js b/lib/assets/javascripts/views/plans/edit_details.js
new file mode 100644
index 0000000..85514c0
--- /dev/null
+++ b/lib/assets/javascripts/views/plans/edit_details.js
@@ -0,0 +1,14 @@
+import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
+import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
+import { Tinymce } from '../../utils/tinymce';
+import ariatiseForm from '../../utils/ariatiseForm';
+
+$(() => {
+ $('[data-toggle="tooltip"]').tooltip();
+ $('[data-toggle="popover"]').popover();
+ Tinymce.init();
+ $('#is_test').click(() => {
+ $('#plan_visibility').val($(this).is(':checked') ? 'is_test' : 'privately_visible');
+ });
+ ariatiseForm({ selector: '.edit_plan' });
+});
diff --git a/lib/assets/karma.conf.js b/lib/assets/karma.conf.js
index 3e95689..6e5f5d4 100644
--- a/lib/assets/karma.conf.js
+++ b/lib/assets/karma.conf.js
@@ -10,8 +10,7 @@
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
- frameworks: ['jasmine'],
-
+ frameworks: ['jasmine', 'jquery-3.2.1'],
// list of files / patterns to load in the browser
files: [
diff --git a/lib/assets/package.json b/lib/assets/package.json
index 4d13c5e..9324098 100644
--- a/lib/assets/package.json
+++ b/lib/assets/package.json
@@ -46,6 +46,7 @@
"karma": "^1.7.0",
"karma-chrome-launcher": "^2.2.0",
"karma-jasmine": "^1.1.0",
+ "karma-jquery": "^0.2.2",
"karma-webpack": "^2.0.4",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
diff --git a/lib/assets/webpack.config.js b/lib/assets/webpack.config.js
index 0011906..71f1ec7 100644
--- a/lib/assets/webpack.config.js
+++ b/lib/assets/webpack.config.js
@@ -15,7 +15,7 @@
context: __dirname,
entry: {
- vendor: ['jquery', 'tinymce/tinymce', 'tinymce/themes/modern/theme'],
+ vendor: ['jquery'],
application: ['./javascripts/application.js', './stylesheets/application.scss'],
},
@@ -47,7 +47,7 @@
}),
},
{
- test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/,
+ test: /(?:fonts\/bootstrap\/.*)|(?:font-awesome\/fonts\/.*)(?:\.woff2?$|\.ttf$|\.eot$|\.svg$)/,
use: [
{
loader: 'file-loader',
@@ -64,14 +64,14 @@
plugins: [
extractSass,
+ new webpack.ProvidePlugin({ // Load jquery module automatically instead of import everywhere
+ jQuery: 'jquery',
+ $: 'jquery',
+ }),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity,
}),
- new webpack.ProvidePlugin({ // Load jquery module automatically instead of import everywhere
- $: 'jquery',
- jQuery: 'jquery',
- }),
new CopyWebPackPlugin([ // Copies every file under images or videos
{ from: './images/**/*', to: `${destPath}/` },
{ from: './videos/**/*', to: `${destPath}/` },