Source: public/src/others/mediaViews.js

/** @module others/mediaViews */

import { findDeep, hooks } from '../util';
import { draggable } from '../util/dragdrop';
import wp from 'wp';
import { filter } from './filter';
import $ from 'jquery';

/**
 * The attachments browser selectors.hooks
 */
export const BROWSER_SELECTOR = '.attachments-browser';

/**
 * This deferred promise is resolved when the first attachments browser'
 * toolbar is created with the RML filter.
 */
export const firstCreatedToolbar = $.Deferred();

/**
 * Modify the media-views.js components (Backbone) in a way
 * to make them compatible with the AIOT component.
 */
export default function() {
    if (!findDeep(window, 'wp.media.view.Attachment.Library')) {
    	return false;
    }
    
    // Create filter
    const RMLFilter = wp.media.view.AttachmentFilters.RML = wp.media.view.AttachmentFilters.extend(filter);
    
    // Hold selectedId to select previously selected id
    const Modal = wp.media.view.Modal;
    wp.media.view.Modal = wp.media.view.Modal.extend({
    	close() {
    		Modal.prototype.close.apply(this, arguments);
    		const { $RmlAppTree } = this.controller;
    		this._rmlFolder = $RmlAppTree ? $RmlAppTree.props.store.selectedId : undefined;
    	},
    	
    	open() {
    		const { $RmlAppTree } = this.controller;
    		if (this._rmlFolder !== undefined && $RmlAppTree) {
    			// Detect, if the folder is available, if yes, select it if not yet
    			const { store } = $RmlAppTree.props,
    				node = store.getTreeItemById(this._rmlFolder);
    			if (node && node.visible && store.selectedId !== this._rmlFolder) {
    				node.setter(node => { node.selected = true; });
    			}
    			
    			// If the folder is no longer available, force 'All' files to be selected
    			if (!node || !node.visible) {
    				$RmlAppTree.handleSelect('all');
    			}
    		}
    		Modal.prototype.open.apply(this, arguments);
    	}
    });
    
    // Allow rml orderby
    wp.media.model.Query.orderby.allowed.push('rml');
    
    // Create rml orderby comparator
    const Attachments = wp.media.view.Attachments;
	wp.media.view.Attachments = wp.media.view.Attachments.extend({
		initialize() {
			Attachments.prototype.initialize.apply(this,arguments);
			const that = this, { collection } = this,
				{ comparator } = collection,
				available = typeof comparator === 'function';
			collection.comparator = function(a, b, c) {
				let aO, bO;
				if (collection.props.get('orderby') === 'rml' && (aO = a.attributes.rmlGalleryOrder)
					&& (bO = b.attributes.rmlGalleryOrder) && aO !== -1 && bO !== -1) {
			        if (aO < bO) {
			            return -1;
			        }else if (aO > bO) {
			            return 1;
			        }
			        return 0;
				}else if (available) {
					return comparator.apply(this, arguments);
				}
			};
			
			// Initially load a folder from the AppTree initialSelectedId state
			const oldMore = collection.more;
			let setted = false;
			collection.more = function() {
				const { $RmlAppTree } = that.controller,
					toolbar = that.views.parent.toolbar;
				let initialSelectedId;
				if ($RmlAppTree && $RmlAppTree.props && (initialSelectedId = $RmlAppTree.initialSelectedId)) {
					if (!setted) {
						const model = toolbar.get('rml_folder').model;
						model.set({ rml_folder: initialSelectedId === 'all' ? '' : initialSelectedId }, { silent: false });
						setted = true;
					}
					return oldMore.apply(this, arguments);
				}
				return $.Deferred().resolveWith(that).promise();
			};
		}
	});
    
    // Call a grid render for all new generated items
	const oldRender = wp.media.view.Attachment.Library.prototype.render;
	wp.media.view.Attachment.Library.prototype.render = function() {
		oldRender.apply(this, arguments);
		const { $RmlAppTree } = this.controller;
		
		/**
         * Fired when an attachments browser item is rendered.
         * 
         * @event module:util/hooks#attachmentsBrowser/item/rendered
         * @param {jQuery} $el The element
         * @param {object} model The backbone model
         * @param {object} appTree The app tree instance
         * @this wp.media.view.Attachment.Library
         */
		hooks.call('attachmentsBrowser/item/rendered', [ this.$el, this.model, $RmlAppTree ], this);
	};
    
    // Modify attachments browser
    const AttachmentsBrowser = wp.media.view.AttachmentsBrowser;
    wp.media.view.AttachmentsBrowser = wp.media.view.AttachmentsBrowser.extend({
		initialize() {
			AttachmentsBrowser.prototype.initialize.apply(this, arguments);
			
			// Events for attachments browsers collections
			let timeout;
			this.collection.on('change reset add remove', () => {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    // Merged collection change
                    const { $RmlAppTree } = this.controller;
                    if ($RmlAppTree) {
	                    draggable($RmlAppTree);
	                    
	                    /**
				         * Fired when the collection of attachments browser changes.
				         * 
				         * @event module:util/hooks#attachmentsBrowser/collection/change
				         * @param {object} appTree The app tree instance
				         * @this wp.media.view.AttachmentsBrowser
				         */
	                    hooks.call('attachmentsBrowser/collection/change', [ $RmlAppTree ], this);
                    }
                }, 50);
            });
            
            this.collection.on('remove', (...args) => {
            	/**
		         * Fired when an attachments browser item gets removed.
		         * 
		         * @event module:util/hooks#attachmentsBrowser/item/removed
		         * @param {mixed} args... The event arguments
		         * @this wp.media.view.AttachmentsBrowser
		         */
				hooks.call('attachmentsBrowser/item/removed', [this.controller.$RmlAppTree, ...args], this);
			});
			
			// Listen to the ajax complete to refresh the folder counts
	    	$(document).ajaxComplete((e, xhs, req) => {
	    	    try {
	    	        if (req.data.indexOf('action=delete-post') > -1) {
	    	            const { $RmlAppTree } = this.controller;
	    	            $RmlAppTree && $RmlAppTree.fetchCounts();
	    	        }
	    	    }catch(e) {
	    	    	// Silence is golden.
	    	    }
	    	});
		},
		
		createToolbar() {
		    AttachmentsBrowser.prototype.createToolbar.call(this);
	        this.$el.data('backboneView', this);
	        
	        // Add new toolbar
	        const obj = new RMLFilter({
				controller: this.controller,
				model: this.collection.props,
				priority: -81 // see media-views.js#7295
			}).render();
			this.toolbar.set('rml_folder', obj);
			obj.initialize();
			
			const { modal } = this.controller.options;
			if (modal) {
				/**
		         * Fired, when a new modal window is created.
		         * 
		         * @event module:util/hooks#attachmentsBrowser/modal
		         * @this wp.media.view.AttachmentsBrowser
		         */
				hooks.call('attachmentsBrowser/modal', [], this);
			}else{
				firstCreatedToolbar.resolve(this);
			}
	    },
	    
	    remove() {
	    	this.controller.$RmlAppTree.handleDestroy();
	    	AttachmentsBrowser.prototype.remove.apply(this, arguments);
	    }
    });
    
    return true;
}

/**
 * Enhanced Media Library compatibility and layout adjustment.
 */
hooks.register('ready', () => {
	if ($('body').hasClass('eml-grid')) {
        const mediaGrid = $('#wp-media-grid'),
            offsetTop = mediaGrid.offset().top,
            fnResize = () => { mediaGrid.css('height', $(window).height() - $('#wpadminbar').height() - 10); },
            fnScroll = () => {
                const scrollTop = $(window).scrollTop();
                mediaGrid[0].style.top = (scrollTop > offsetTop ? scrollTop : 0) + 'px';
                //mediaGrid.css("top", scrollTop > offsetTop ? scrollTop - offsetTop : 0);
            };
    
        // Centerize container
        $(window).on("resize", fnResize);
        fnResize();
        
        // Scroll container
        $(window).on("scroll", fnScroll);
        fnScroll();
    }
});