/**
 * Handles UX and Events for inline editing of views
 *
 * Use with a Model's View
 * Allows editing model.title field via .llms-editable-title elements
 *
 * @package LifterLMS/Scripts
 *
 * @since 3.16.0
 * @since 3.25.4 Unknown
 * @since 3.37.11 Replace reference to `wp.editor` with `_.getEditor()` helper.
 * @version 3.37.11
 */
define( [], function() {

	return {

		media_lib: null,

		/**
		 * DOM Events
		 *
		 * @type  {Object}
		 * @since    3.16.0
		 * @version  3.17.8
		 */
		events: {
			'click .llms-add-image': 'open_media_lib',
			'click a[href="#llms-edit-slug"]': 'make_slug_editable',
			'click a[href="#llms-remove-image"]': 'remove_image',
			'change .llms-editable-select select': 'on_select',
			'change .llms-switch input[type="checkbox"]': 'toggle_switch',
			'change .llms-editable-radio input': 'on_radio_select',
			'focusin .llms-input': 'on_focus',
			'focusout .llms-input': 'on_blur',
			'keydown .llms-input': 'on_keydown',
			'input .llms-input[type="number"]': 'on_blur',
			'paste .llms-input[data-formatting]': 'on_paste',
		},

		/**
		 * Retrieve a list of allowed tags for a given element
		 *
		 * @param    obj   $el  jQuery selector for the element
		 * @return   array
		 * @since    3.16.0
		 * @version  3.17.8
		 */
		get_allowed_tags: function( $el ) {

			if ( $el.attr( 'data-formatting' ) ) {
				return _.map( $el.attr( 'data-formatting' ).split( ',' ), function( tag ) {
					return tag.trim();
				} );
			}

			return [ 'b', 'i', 'u', 'strong', 'em' ];

		},

		/**
		 * Retrieve the content of an element
		 *
		 * @param    obj   $el  jQuery object of the element
		 * @return   string
		 * @since    3.16.0
		 * @version  3.17.8
		 */
		get_content: function( $el ) {

			if ( 'INPUT' === $el[0].tagName ) {
				return $el.val();
			}

			if ( ! $el.attr( 'data-formatting' ) && ! $el.hasClass( 'ql-editor' ) ) {
				return $el.text();
			}

			return _.stripFormatting( $el.html(), this.get_allowed_tags( $el ) );

		},

		/**
		 * Determine if changes have been made to the element
		 *
		 * @param    {[obj]}   event  js event object
		 * @return   {Boolean}        true when changes have been made, false otherwise
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		has_changed: function( event ) {
			var $el = $( event.target );
			return ( $el.attr( 'data-original-content' ) !== this.get_content( $el ) );
		},

		/**
		 * Ensure that new content is at least 1 character long
		 *
		 * @param    obj   event  js event object
		 * @return   boolean
		 * @since    3.16.0
		 * @version  3.17.2
		 */
		is_valid: function( event ) {

			var self    = this,
				$el     = $( event.target ),
				content = this.get_content( $el ),
				type    = $el.attr( 'data-type' );

			if ( ( $el.attr( 'required' ) || $el.attr( 'data-required' ) ) && content.length < 1 ) {
				return false;
			}

			if ( 'url' === type || 'video' === type ) {
				if ( ! this._validate_url( this.get_content( $el ) ) ) {
					return false;
				}

			} else if ( 'permalink' === type ) {

				LLMS.Ajax.call( {
					data: {
						action: 'llms_builder',
						action_type: 'get_permalink',
						course_id: window.llms_builder.CourseModel.get( 'id' ),
						id: self.model.get( 'id' ),
						title: self.model.get( 'title' ),
						slug: content,
					},
					beforeSend: function() {
						LLMS.Spinner.start( $el.closest( '.llms-editable-toggle-group' ), 'small' );
					},
					success: function( r ) {

						if ( r.permalink && r.slug ) {
							self.model.set( 'permalink', r.permalink );
							self.model.set( 'name', r.slug );
							self.render();
						}

					}
				} );

			}

			return true;

		},

		/**
		 * Initialize datepicker elements
		 *
		 * @return   void
		 * @since    3.17.0
		 * @version  3.17.0
		 */
		init_datepickers: function() {

			this.$el.find( '.llms-editable-date input' ).each( function() {

				$( this ).datetimepicker( {
					format: $( this ).attr( 'data-date-format' ) || 'Y-m-d h:i A',
					datepicker: ( undefined === $( this ).attr( 'data-date-datepicker' ) ) ? true : ( 'true' == $( this ).attr( 'data-date-datepicker' ) ),
					timepicker: ( undefined === $( this ).attr( 'data-date-timepicker' ) ) ? true : ( 'true' == $( this ).attr( 'data-date-timepicker' ) ),
					onClose: function( current_time, $input ) {
						$input.blur();
					}
				} );

			} );

		},

		/**
		 * Initialize elements that allow inline formatting
		 *
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		init_formatting_els: function() {

			var self = this;

			this.$el.find( '.llms-input-formatting[data-formatting]' ).each( function() {

				var formatting = $( this ).attr( 'data-formatting' ).split( ',' ),
					attr       = $( this ).attr( 'data-attribute' );

				var ed = new Quill( this, {
					modules: {
						toolbar: [ formatting ],
						keyboard: {
							bindings: {
								tab: {
									key: 9,
									handler: function( range, context ) {
										return true;
									},
								},
								13: {
									key: 13,
									handler: function( range, context ) {
										ed.root.blur();
										return false;
									},
								},
							},
						},
					},
					placeholder: $( this ).attr( 'data-placeholder' ),
					theme: 'bubble',
				} );

				ed.on( 'text-change', function( delta, oldDelta, source ) {
					self.model.set( attr, self.get_content( $( ed.root ) ) );
				} );

				Backbone.pubSub.trigger( 'formatting-ed-init', ed, $( this ), self );

			} );

		},

		/**
		 * Initialize editable select elements
		 *
		 * @return   void
		 * @since    3.16.0
		 * @version  3.25.4
		 */
		init_selects: function() {

			this.$el.find( '.llms-editable-select select' ).llmsSelect2( {
				width: '100%',
			} ).trigger( 'change' );

		},

		/**
		 * Blur/focusout function for .llms-editable-title elements
		 * Automatically saves changes if changes have been made
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.6
		 */
		on_blur: function( event ) {

			event.stopPropagation();

			this.model.set( '_has_focus', false, { silent: true } );

			var self    = this,
				$el     = $( event.target ),
				changed = this.has_changed( event );

			if ( changed ) {

				if ( ! self.is_valid( event ) ) {
					self.revert_edits( event );
				} else {
					this.save_edits( event );
				}

			}

		},

		/**
		 * Focus event for editable inputs
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.6
		 * @version  3.16.6
		 */
		on_focus: function( event ) {

			event.stopPropagation();
			this.model.set( '_has_focus', true, { silent: true } );

		},

		/**
		 * Handle content pasted into contenteditable fields
		 * This will ensure that HTML from RTF editors isn't pasted into the dom
		 *
		 * @param    obj   event  js event obj
		 * @return   void
		 * @since    3.17.8
		 * @version  3.17.8
		 */
		on_paste: function( event ) {

			event.preventDefault();
			event.stopPropagation();

			var text = ( event.originalEvent || event ).clipboardData.getData( 'text/plain' );
			window.document.execCommand( 'insertText', false, text );

		},

		/**
		 * Change event for selectables
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		on_select: function( event ) {

			var $el       = $( event.target ),
				multi     = ( $el.attr( 'multiple' ) ),
				attr      = $el.attr( 'name' ),
				$selected = $el.find( 'option:selected' ),
				val;

			if ( multi ) {
				val = [];
				val = $selected.map( function() {
					return this.value;
				} ).get();
			} else {
				val = $selected[0].value;
			}

			this.model.set( attr, val );

		},

		/**
		 * Change event for radio element groups
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.17.6
		 * @version  3.17.6
		 */
		on_radio_select: function( event ) {

			var $el  = $( event.target ),
				attr = $el.attr( 'name' ),
				val  = $el.val();

			this.model.set( attr, val );

		},

		/**
		 * Keydown function for .llms-editable-title elements
		 * Blurs
		 *
		 * @param    {obj}   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.17.8
		 */
		on_keydown: function( event ) {

			event.stopPropagation();

			var self  = this,
				key   = event.which || event.keyCode,
				shift = event.shiftKey;
				// ctrl = event.metaKey || event.ctrlKey;

			switch ( key ) {

				case 13: // enter
					// shift + enter should add a return
					if ( ! shift ) {
						event.preventDefault();
						event.target.blur();
					}
				break;

				case 27: // escape
					event.preventDefault();
					this.revert_edits( event );
					event.target.blur();
				break;

			}

		},

		/**
		 * Open the WP media lib
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.6
		 */
		open_media_lib: function( event ) {

			event.stopPropagation();

			var self = this,
				$el  = $( event.currentTarget );

			if ( self.media_lib ) {

				self.media_lib.uploader.uploader.param( 'post_id' );

			} else {

				self.media_lib = wp.media.frames.file_frame = wp.media( {
					title: LLMS.l10n.translate( 'Select an image' ),
					button: {
						text: LLMS.l10n.translate( 'Use this image' ),
					},
					multiple: false	// Set to true to allow multiple files to be selected
				} );

				self.media_lib.on( 'select', function() {

					var size       = $el.attr( 'data-image-size' ),
						attachment = self.media_lib.state().get( 'selection' ).first().toJSON(),
						image      = self.model.get( $el.attr( 'data-attribute' ) ),
						url;

					if ( size && attachment.sizes[ size ] ) {
						url = attachment.sizes[ size ].url;
					} else {
						url = attachment.url;
					}

					image.set( {
						id: attachment.id,
						src: url,
					} );

				} );

			}

			// This flag is used to protect the media files uploaded via places like the Quiz questions (Picture type)
			self.media_lib.uploader.options.uploader.params.llms = 1;

			self.media_lib.open();

		},

		/**
		 * Click event to remove an image
		 *
		 * @param    obj   event  js event obj
		 * @return   voids
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		remove_image: function( event ) {

			event.preventDefault();

			this.model.get( $( event.currentTarget ).attr( 'data-attribute' ) ).set( {
				id: '',
				src: '',
			} );

		},

		/**
		 * Helper to undo changes
		 * Bound to "escape" key via on_keydown function
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		revert_edits: function( event ) {
			var $el = $( event.target ),
				val = $el.attr( 'data-original-content' );
			$el.html( val );
		},

		/**
		 * Sync changes to the model and DB
		 *
		 * @param    {obj}   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.16.0
		 */
		save_edits: function( event ) {

			var $el = $( event.target ),
				val = this.get_content( $el );

			this.model.set( $el.attr( 'data-attribute' ), val );

		},

		/**
		 * Change event for a switch element
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.0
		 * @version  3.17.0
		 */
		toggle_switch: function( event ) {

			event.stopPropagation();
			var $el      = $( event.target ),
				attr     = $el.attr( 'name' ),
				rerender = $el.attr( 'data-rerender' ),
				val;

			if ( $el.is( ':checked' ) ) {
				val = $el.attr( 'data-on' ) ? $el.attr( 'data-on' ) : 'yes';
			} else {
				val = $el.attr( 'data-off' ) ? $el.attr( 'data-off' ) : 'no';
			}

			if ( -1 !== attr.indexOf( '.' ) ) {

				var split = attr.split( '.' );

				if ( 'parent' === split[0] ) {
					this.model.get_parent().set( split[1], val );
				} else {
					this.model.get( split[0] ).set( split[1], val );
				}

			} else {

				this.model.set( attr, val );

			}

			this.trigger( attr.replace( '.', '-' ) + '_toggle', val );

			if ( ! rerender || 'yes' === rerender ) {
				var self = this;
				setTimeout( function() {
					self.render();
				}, 100 );
			}

		},

		/**
		 * Initializes a WP Editor on a textarea
		 *
		 * @since 3.16.0
		 * @since 3.37.11 Replace reference to `wp.editor` with `_.getEditor()` helper.
		 *
		 * @param {String} id        CSS ID of the editor (don't include #).
		 * @param {Object} settings  Optional object of settings to pass to wp.oldEditor.initialize().
		 * @return {Void}
		 */
		init_editor: function( id, settings ) {

			settings = settings || {};

			var editor = _.getEditor();

			editor.remove( id );

			editor.initialize( id, $.extend( true, editor.getDefaultSettings(), {
				mediaButtons: true,
				tinymce: {
					toolbar1: 'bold,italic,strikethrough,bullist,numlist,blockquote,hr,alignleft,aligncenter,alignright,link,unlink,wp_adv',
					toolbar2: 'formatselect,underline,alignjustify,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
					setup: _.bind( this.on_editor_ready, this ),
				}
			}, settings ) );

		},

		/**
		 * Setup a permalink editor to allow editing of a permalink
		 *
		 * @param    obj   event  js event object
		 * @return   void
		 * @since    3.16.6
		 * @version  3.16.6
		 */
		make_slug_editable: function( event ) {

			var self      = this,
				$btn      = $( event.currentTarget ),
				$link     = $btn.prevAll( 'a' ),
				$input    = $btn.prev( 'input.permalink' ),
				full_url  = $link.attr( 'href' ),
				slug      = $input.val(),
				short_url = full_url.replace( slug, '' );

			// hide the button
			$btn.hide();

			// make the link not clickable
			$link.css( {
				color: '#999',
				'pointer-events': 'none',
				'text-decoration': 'none',
			} );

			// remove the current slug & trailing slash from the URL
			$link.text( short_url.substring( 0, short_url.length - 1 ) );

			// focus in on the field
			$input.show().focus();

		},

		/**
		 * Callback function called after initialization of an editor
		 *
		 * Updates UI if a label is present.
		 *
		 * Binds a change event to ensure editor changes are saved to the model.
		 *
		 * @since 3.16.0
		 * @since 3.17.1 Uknown.
		 * @since 3.37.11 Replace references to `wp.editor` with `_.getEditor()` helper.
		 *
		 * @param {Object} editor TinyMCE Editor instance.
		 * @return {Void}
		 */
		on_editor_ready: function( editor ) {

			var self    = this,
				$ed     = $( '#' + editor.id ),
				$parent = $ed.closest( '.llms-editable-editor' ),
				$label  = $parent.find( '.llms-label' ),
				prop    = $ed.attr( 'data-attribute' )

			if ( $label.length ) {
				$label.prependTo( $parent.find( '.wp-editor-tools' ) );
			}

			// save changes to the model via Visual ed
			editor.on( 'change', function( event ) {
				self.model.set( prop, _.getEditor().getContent( editor.id ) );
			} );

			// save changes via Text ed
			$ed.on( 'input', function( event ) {
				self.model.set( prop, $ed.val() );
			} );

			// trigger an input on the Text ed when quicktags buttons are clicked
			$parent.on( 'click', '.quicktags-toolbar .ed_button', function() {
				setTimeout( function() {
					$ed.trigger( 'input' );
				}, 10 );
			} );

		},

		_validate_url: function( str ) {

			var a  = document.createElement( 'a' );
			a.href = str;
			return ( a.host && a.host !== window.location.host );

		}

	};

} );
