/** * USOF Field: Upload */ ! function( $, _undefined ) { const abs = Math.abs; if ( $ush.isUndefined( window.$usof ) ) { return; } /** * @type {{}} Types of previews displayed. */ const PREVIEW_TYPE = { DYNAMIC_VALUE: 'dynamic_value', IMAGE: 'image', TEXT: 'text', }; $usof.field[ 'upload' ] = { init: function( options ) { const self = this; self.parentInit( options ); // Elements self.$container = $( '.usof-upload', self.$row ); self.$preview = $( '.usof-upload-preview', self.$row ); // Private "Variables" self._dynamicLabels = {}; self.hasDynamicValues = $( '.usof-popup', self.$row ).length; self.i18n = {}; self.isMultiple = self.$container.hasClass( 'is_multiple' ); self.placeholder = $( 'input[name="placeholder"]', self.$row ).val(); self.popup = {}; // Get preview type by default self._previewType = self.$container.data( 'preview-type' ); self.$container.removeAttr( 'data-preview-type' ); // Load internationalization var $i18n = $( '.usof-upload-i18n', self.$row ); if ( $i18n.length > 0 ) { self.i18n = $i18n[0].onclick() || {}; $i18n.remove(); } // Bondable events self._events = { removeItem: self._removeItem.bind( self ), selectedInMediaLibrary: self._selectedInMediaLibrary.bind( self ), showMediaLibrary: self._showMediaLibrary.bind( self ), showSelectedInMediaLibrary: self._showSelectedInMediaLibrary.bind( self ), showSelectionWindow: self._showSelectionWindow.bind( self ), }; // Events self.$row // Remove item from selected. .on( 'click', '.ui-icon_close', self._events.removeItem ) // Opens a media uploader. .on( 'click', '.ui-icon_add', self._events.showMediaLibrary ); // Show selection window depending on preview type. if ( ! self.isMultiple ) { self.$row.on( 'click', '.usof-upload-preview-file', self._events.showSelectionWindow ); } // Init Drag & Drop if ( self.isMultiple && self.getPreviewType() === PREVIEW_TYPE.IMAGE ) { self.$body = $( document.body ); self.$window = $( window ); self.$dragShadow = $( '
' ); $.extend( self._events, { dragEnd: self._dragEnd.bind( self ), dragMove: self._dragMove.bind( self ), dragStart: self._dragStart.bind( self ), maybeDragMove: self._maybeDragMove.bind( self ), } ); self.$row .on( 'mousedown', '.usof-upload-preview-file', self._events.dragStart ) // Preventing browser native drag event. .on( 'dragstart', ( e ) => { e.preventDefault() } ); } // If there are dynamic values, then there must be a popup window if ( self.hasDynamicValues ) { self.$variables = $( '.usof-upload-variables', self.$row ); self.$dynamicValue = $( '.usof-upload-dynamic-value', self.$row ); self._events.selectDynamicValue = self.selectDynamicValue.bind( self ); // Events self.$preview.on( 'click', '.usof-upload-preview-file', ( e ) => { if ( self.getPreviewType() === PREVIEW_TYPE.DYNAMIC_VALUE ) { self._showSelectionWindow( e ); } } ); // Set a unique popup id (this is necessary for groups like Dropdown) self.popupId = $ush.uniqid(); $( '[data-popup-id]', self.$row ).attr( 'data-popup-id', self.popupId ); $( '[data-popup-show]', self.$row ).attr( 'data-popup-show', self.popupId ); // Create a new popup self.popup = new $usof.popup( self.popupId, { closeOnEsc: true, // close the popup by pressing Escape closeOnBgClick: true, // close the popup when user clicks on the dark overlay // Fires after first initialization init: function() { /*popup*/this.$container .off( 'click' ) .on( 'click', '[data-dynamic-value]', self._events.selectDynamicValue ) .find( '[data-dynamic-value]' ) .each( ( _, node ) => { const $node = $( node ); if ( $node.data( 'dynamic-label' ) ) { self._dynamicLabels[ $node.data( 'dynamic-value' ) ] = $node.data( 'dynamic-label' ); $node.removeAttr( 'data-dynamic-label' ); } } ); }, // Handler is called before the popup show beforeShow: function() { var that = this; // Set or remove active class $( '[data-dynamic-value]', that.$container ).removeClass( 'active' ); $ush.toString( self.getValue() ) .split( ',' ) .map( ( value ) => { $( `[data-dynamic-value="${value}"]`, that.$container ).addClass( 'active' ); } ); } } ); // Check the initialization of the popup if ( $.isEmptyObject( self.popup ) ) { console.error( 'Failed to initialize popup' ); } } }, /** * Determine whether any of the matched elements are assigned the given class. * Note:Specific method for fixing a bug. * * @param {Node} node The node from document. * @param {String} className The class name one or more separated by a space. * @return {Boolean} True, if there is at least one class, False otherwise. */ _hasClass: function( node, className ) { return $ush.isNode( node ) && ( ' ' + node.className + ' ' ).indexOf( ' ' + className + ' ' ) > - 1; }, /** * Get current preview type. * * @return {String} Returns the current preview type. */ getPreviewType: function() { return this.$container.usMod( 'preview' ); }, /** * Set the preview type. * * @param {String} type The new type. */ setPreviewType: function( type ) { this.$container.usMod( 'preview', $ush.toString( type ) ); }, /** * Show selection window depending on preview type. * * @event handler * @param {Event} e The Event interface represents an event which takes place in the DOM. */ _showSelectionWindow: function( e ) { const self = this; // If these are deletions, then we exit if ( self._hasClass( e.target, 'ui-icon_close' ) ) { return; } if ( self.getPreviewType() === PREVIEW_TYPE.DYNAMIC_VALUE ) { $( 'button.for_select_dynamic_value', self.$row ).trigger( 'click' ); } else { self._showMediaLibrary( e ); } }, /** * Show Media Library. * * @event handler * @param {Event} e The Event interface represents an event which takes place in the DOM. */ _showMediaLibrary: function( e ) { const self = this; var mediaInstance = self.mediaInstance; // If these are deletions, then we exit if ( self._hasClass( e.target, 'ui-icon_close' ) ) { return; } if ( $ush.isUndefined( mediaInstance ) ) { var mediaSettings = { multiple: self.isMultiple ? 'add' : false, }; if ( self.getPreviewType() === PREVIEW_TYPE.IMAGE ) { mediaSettings.library = { type: 'image' }; } mediaInstance = wp.media( mediaSettings ); mediaInstance.on( 'open', self._events.showSelectedInMediaLibrary ); mediaInstance.on( 'select', self._events.selectedInMediaLibrary ); self.mediaInstance = mediaInstance; } mediaInstance.open(); }, /** * Handler for show selected attachments in Media Library. * * @event handler */ _showSelectedInMediaLibrary: function() { const self = this; const mediaInstance = self.mediaInstance; mediaInstance.state().get( 'selection' ).reset(); $ush.toString( self.getValue() ) .split( ',' ) .map( ( attachmentId, i ) => { if ( ! self.isMultiple && i > 0 ) { return; } mediaInstance.state().get( 'selection' ).add( wp.media.attachment( $ush.parseInt( attachmentId ) ) ); } ); }, /** * Handler for set selected attachments from Media Library. * * @event handler */ _selectedInMediaLibrary: function() { const self = this; const mediaInstance = self.mediaInstance; var value = []; if ( self.isMultiple ) { mediaInstance.state().get( 'selection' ).each( ( attachment ) => { if ( attachment.id ) { value.push( attachment.id ); } } ); } else { const attachment = mediaInstance.state().get( 'selection' ).first(); if ( attachment.attributes.url ) { value = [ attachment.id ]; } } // For WPBakery image filters if ( window.vc_selectedFilters && window.vcAdminNonce ) { $.ajax( { type: 'POST', url: $usof.ajaxUrl, dataType: 'json', data: { action: 'vc_media_editor_add_image', filters: window.vc_selectedFilters, ids: value, _vc_inline: true, _vcnonce: window.vcAdminNonce }, success: ( res ) => { if ( res.data.ids.length ) { self.setValue( res.data.ids.toString() ); } }, complete: () => { window.vc_selectedFilters = _undefined; }, } ); } else { self.setValue( value.toString() ); } }, /** * Remove item from selected. * * @event handler * @param {Event} e The Event interface represents an event which takes place in the DOM. */ _removeItem: function( e ) { const self = this; const $previewFile = $( e.target ).closest( '.usof-upload-preview-file' ); const removeValue = $ush.toString( $previewFile.data( 'value' ) ); if ( removeValue ) { var value = $ush.parseInt( removeValue ) !== -1 // -1 installed on demos for placeholder ? $ush.toString( self.getValue() ) : ''; if ( value ) { value = value .split( ',' ) .filter( ( value ) => { return value !== removeValue } ) .toString(); } if ( value ) { self.parentSetValue( value ); } else { self.setValue( '' ); } $previewFile.remove(); } }, /** * Handler for select a dynamic value in a popup. * * @event handler * @param {Event} e The Event interface represents an event which takes place in the DOM. */ selectDynamicValue: function( e ) { const self = this; e.preventDefault(); // Update preview type and set value self.setPreviewType( PREVIEW_TYPE.DYNAMIC_VALUE ); self.setValue( $ush.toString( e.target.dataset['dynamicValue'] ) ); // Hide a popup by its id if ( self.popupId ) { $usof.hidePopup( $ush.toString( self.popupId ) ); } }, /** * Render preview. * * @param {String|undefined} THe URL address or plain text. * @param {String} value The id or dynamic value. * @param {Boolean} removeButton Presence of a remove button. * @param {Numeric} index Preview index in the list if any. */ renderPreview: function( text, value, removeButton, index ) { const self = this; text = String( text ); if ( ! text ) { return } var newPreview = ''; const previewType = self.getPreviewType(); const isPlaceholder = ( text === ':placeholder' ); // Create new preview if ( previewType === PREVIEW_TYPE.DYNAMIC_VALUE ) { newPreview = '' + $ush.toString( self._dynamicLabels[ value ] ) + ''; } else if ( previewType === PREVIEW_TYPE.IMAGE ) { if ( ! isPlaceholder ) { newPreview = '