Вход Регистрация
Файл: wordpress/wp-includes/js/mce-view.js
Строк: 1121
<?php
/* global tinymce */

window.wp window.wp || {};

/*
 * The TinyMCE view API.
 *
 * Note: this API is "experimental" meaning that it will probably change
 * in the next few releases based on feedback from 3.9.0.
 * If you decide to use it, please follow the development closely.
 *
 * Diagram
 *
 * |- registered view constructor (type)
 * |  |- view instance (unique text)
 * |  |  |- editor 1
 * |  |  |  |- view node
 * |  |  |  |- view node
 * |  |  |  |- ...
 * |  |  |- editor 2
 * |  |  |  |- ...
 * |  |- view instance
 * |  |  |- ...
 * |- registered view
 * |  |- ...
 */
( function( windowwp, $ ) {
    
'use strict';

    var 
views = {},
        
instances = {};

    
wp.mce wp.mce || {};

    
/**
     * wp.mce.views
     *
     * A set of utilities that simplifies adding custom UI within a TinyMCE editor.
     * At its core, it serves as a series of converters, transforming text to a
     * custom UI, and back again.
     */
    
wp.mce.views = {

        
/**
         * Registers a new view type.
         *
         * @param {String} type   The view type.
         * @param {Object} extend An object to extend wp.mce.View.prototype with.
         */
        
register: function( typeextend ) {
            
viewstype ] = wp.mce.View.extend_.extendextend, { typetype } ) );
        },

        
/**
         * Unregisters a view type.
         *
         * @param {String} type The view type.
         */
        
unregister: function( type ) {
            
delete viewstype ];
        },

        
/**
         * Returns the settings of a view type.
         *
         * @param {String} type The view type.
         *
         * @return {Function} The view constructor.
         */
        
get: function( type ) {
            return 
viewstype ];
        },

        
/**
         * Unbinds all view nodes.
         * Runs before removing all view nodes from the DOM.
         */
        
unbind: function() {
            
_.eachinstances, function( instance ) {
                
instance.unbind();
            } );
        },

        
/**
         * Scans a given string for each view's pattern,
         * replacing any matches with markers,
         * and creates a new instance for every match.
         *
         * @param {String} content The string to scan.
         *
         * @return {String} The string with markers.
         */
        
setMarkers: function( content ) {
            var 
pieces = [ { contentcontent } ],
                
self this,
                
instance,
                
current;

            
_.eachviews, function( viewtype ) {
                
current pieces.slice();
                
pieces  = [];

                
_.eachcurrent, function( piece ) {
                    var 
remaining piece.content,
                        
result;

                    
// Ignore processed pieces, but retain their location.
                    
if ( piece.processed ) {
                        
pieces.pushpiece );
                        return;
                    }

                    
// Iterate through the string progressively matching views
                    // and slicing the string as we go.
                    
while ( remaining && ( result view.prototype.matchremaining ) ) ) {
                        
// Any text before the match becomes an unprocessed piece.
                        
if ( result.index ) {
                            
pieces.push( { contentremaining.substring0result.index ) } );
                        }

                        
instance self.createInstancetyperesult.contentresult.options );

                        
// Add the processed piece for the match.
                        
pieces.push( {
                            
content'<p data-wpview-marker="' instance.encodedText '">' instance.text '</p>',
                            
processedtrue
                        
} );

                        
// Update the remaining content.
                        
remaining remaining.sliceresult.index result.content.length );
                    }

                    
// There are no additional matches.
                    // If any content remains, add it as an unprocessed piece.
                    
if ( remaining ) {
                        
pieces.push( { contentremaining } );
                    }
                } );
            } );

            return 
_.pluckpieces'content' ).join'' );
        },

        
/**
         * Create a view instance.
         *
         * @param {String} type    The view type.
         * @param {String} text    The textual representation of the view.
         * @param {Object} options Options.
         *
         * @return {wp.mce.View} The view instance.
         */
        
createInstance: function( typetextoptions ) {
            var 
View this.gettype ),
                
encodedText,
                
instance;

            
text tinymce.DOM.decodetext ),
            
encodedText encodeURIComponenttext ),
            
instance this.getInstanceencodedText );

            if ( 
instance ) {
                return 
instance;
            }

            
options _.extendoptions || {}, {
                
texttext,
                
encodedTextencodedText
            
} );

            return 
instancesencodedText ] = new Viewoptions );
        },

        
/**
         * Get a view instance.
         *
         * @param {(String|HTMLElement)} object The textual representation of the view or the view node.
         *
         * @return {wp.mce.View} The view instance or undefined.
         */
        
getInstance: function( object ) {
            if ( 
typeof object === 'string' ) {
                return 
instancesencodeURIComponent( object ) ];
            }

            return 
instances[ $( object ).attr'data-wpview-text' ) ];
        },

        
/**
         * Given a view node, get the view's text.
         *
         * @param {HTMLElement} node The view node.
         *
         * @return {String} The textual representation of the view.
         */
        
getText: function( node ) {
            return 
decodeURIComponent( $( node ).attr'data-wpview-text' ) || '' );
        },

        
/**
         * Renders all view nodes that are not yet rendered.
         *
         * @param {Boolean} force Rerender all view nodes.
         */
        
render: function( force ) {
            
_.eachinstances, function( instance ) {
                
instance.renderforce );
            } );
        },

        
/**
         * Update the text of a given view node.
         *
         * @param {String}         text   The new text.
         * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
         * @param {HTMLElement}    node   The view node to update.
         */
        
update: function( texteditornode ) {
            var 
instance this.getInstancenode );

            if ( 
instance ) {
                
instance.updatetexteditornode );
            }
        },

        
/**
         * Renders any editing interface based on the view type.
         *
         * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
         * @param {HTMLElement}    node   The view node to edit.
         */
        
edit: function( editornode ) {
            var 
instance this.getInstancenode );

            if ( 
instance && instance.edit ) {
                
instance.editinstance.text, function( text ) {
                    
instance.updatetexteditornode );
                } );
            }
        },

        
/**
         * Remove a given view node from the DOM.
         *
         * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
         * @param {HTMLElement}    node   The view node to remove.
         */
        
remove: function( editornode ) {
            var 
instance this.getInstancenode );

            if ( 
instance ) {
                
instance.removeeditornode );
            }
        }
    };

    
/**
     * A Backbone-like View constructor intended for use when rendering a TinyMCE View.
     * The main difference is that the TinyMCE View is not tied to a particular DOM node.
     *
     * @param {Object} options Options.
     */
    
wp.mce.View = function( options ) {
        
_.extendthisoptions );
        
this.initialize();
    };

    
wp.mce.View.extend Backbone.View.extend;

    
_.extendwp.mce.View.prototype, {

        
/**
         * The content.
         *
         * @type {*}
         */
        
contentnull,

        
/**
         * Whether or not to display a loader.
         *
         * @type {Boolean}
         */
        
loadertrue,

        
/**
         * Runs after the view instance is created.
         */
        
initialize: function() {},

        
/**
         * Retuns the content to render in the view node.
         *
         * @return {*}
         */
        
getContent: function() {
            return 
this.content;
        },

        
/**
         * Renders all view nodes tied to this view instance that are not yet rendered.
         *
         * @param {String} content The content to render. Optional.
         * @param {Boolean} force Rerender all view nodes tied to this view instance.
         */
        
render: function( contentforce ) {
            if ( 
content != null ) {
                
this.content content;
            }

            
content this.getContent();

            
// If there's nothing to render an no loader needs to be shown, stop.
            
if ( ! this.loader && ! content ) {
                return;
            }

            
// We're about to rerender all views of this instance, so unbind rendered views.
            
force && this.unbind();

            
// Replace any left over markers.
            
this.replaceMarkers();

            if ( 
content ) {
                
this.setContentcontent, function( editornodecontentNode ) {
                    $( 
node ).data'rendered'true );
                    
this.bindNode.callthiseditornodecontentNode );
                }, 
force null false );
            } else {
                
this.setLoader();
            }
        },

        
/**
         * Binds a given node after its content is added to the DOM.
         */
        
bindNode: function() {},

        
/**
         * Unbinds a given node before its content is removed from the DOM.
         */
        
unbindNode: function() {},

        
/**
         * Unbinds all view nodes tied to this view instance.
         * Runs before their content is removed from the DOM.
         */
        
unbind: function() {
            
this.getNodes( function( editornodecontentNode ) {
                
this.unbindNode.callthiseditornodecontentNode );
                $( 
node ).trigger'wp-mce-view-unbind' );
            }, 
true );
        },

        
/**
         * Gets all the TinyMCE editor instances that support views.
         *
         * @param {Function} callback A callback.
         */
        
getEditors: function( callback ) {
            
_.eachtinymce.editors, function( editor ) {
                if ( 
editor.plugins.wpview ) {
                    
callback.callthiseditor );
                }
            }, 
this );
        },

        
/**
         * Gets all view nodes tied to this view instance.
         *
         * @param {Function} callback A callback.
         * @param {Boolean}  rendered Get (un)rendered view nodes. Optional.
         */
        
getNodes: function( callbackrendered ) {
            
this.getEditors( function( editor ) {
                var 
self this;

                $( 
editor.getBody() )
                    .
find'[data-wpview-text="' self.encodedText '"]' )
                    .
filter( function() {
                        var 
data;

                        if ( 
rendered == null ) {
                            return 
true;
                        }

                        
data = $( this ).data'rendered' ) === true;

                        return 
rendered data : ! data;
                    } )
                    .
each( function() {
                        
callback.callselfeditorthis, $( this ).find'.wpview-content' ).get) );
                    } );
            } );
        },

        
/**
         * Gets all marker nodes tied to this view instance.
         *
         * @param {Function} callback A callback.
         */
        
getMarkers: function( callback ) {
            
this.getEditors( function( editor ) {
                var 
self this;

                $( 
editor.getBody() )
                    .
find'[data-wpview-marker="' this.encodedText '"]' )
                    .
each( function() {
                        
callback.callselfeditorthis );
                    } );
            } );
        },

        
/**
         * Replaces all marker nodes tied to this view instance.
         */
        
replaceMarkers: function() {
            
this.getMarkers( function( editornode ) {
                if ( $( 
node ).text() !== this.text ) {
                    
editor.dom.setAttribnode'data-wpview-marker'null );
                    return;
                }

                
editor.dom.replace(
                    
editor.dom.createFragment(
                        
'<div class="wpview-wrap" data-wpview-text="' this.encodedText '" data-wpview-type="' this.type '">' +
                            
'<p class="wpview-selection-before">u00a0</p>' +
                            
'<div class="wpview-body" contenteditable="false">' +
                                
'<div class="wpview-content wpview-type-' this.type '"></div>' +
                            
'</div>' +
                            
'<p class="wpview-selection-after">u00a0</p>' +
                        
'</div>'
                    
),
                    
node
                
);
            } );
        },

        
/**
         * Removes all marker nodes tied to this view instance.
         */
        
removeMarkers: function() {
            
this.getMarkers( function( editornode ) {
                
editor.dom.setAttribnode'data-wpview-marker'null );
            } );
        },

        
/**
         * Sets the content for all view nodes tied to this view instance.
         *
         * @param {*}        content  The content to set.
         * @param {Function} callback A callback. Optional.
         * @param {Boolean}  rendered Only set for (un)rendered nodes. Optional.
         */
        
setContent: function( contentcallbackrendered ) {
            if ( 
_.isObjectcontent ) && content.body.indexOf'<script' ) !== -) {
                
this.setIframescontent.head || ''content.bodycallbackrendered );
            } else if ( 
_.isStringcontent ) && content.indexOf'<script' ) !== -) {
                
this.setIframes''contentcallbackrendered );
            } else {
                
this.getNodes( function( editornodecontentNode ) {
                    
content content.body || content;

                    if ( 
content.indexOf'<iframe' ) !== -) {
                        
content += '<div class="wpview-overlay"></div>';
                    }

                    
contentNode.innerHTML '';
                    
contentNode.appendChild_.isStringcontent ) ? editor.dom.createFragmentcontent ) : content );

                    
callback && callback.callthiseditornodecontentNode );
                }, 
rendered );
            }
        },

        
/**
         * Sets the content in an iframe for all view nodes tied to this view instance.
         *
         * @param {String}   head     HTML string to be added to the head of the document.
         * @param {String}   body     HTML string to be added to the body of the document.
         * @param {Function} callback A callback. Optional.
         * @param {Boolean}  rendered Only set for (un)rendered nodes. Optional.
         */
        
setIframes: function( headbodycallbackrendered ) {
            var 
MutationObserver window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
                
self this;

            
this.getNodes( function( editornodecontentNode ) {
                var 
dom editor.dom,
                    
styles '',
                    
bodyClasses editor.getBody().className || '',
                    
editorHead editor.getDoc().getElementsByTagName'head' )[0];

                
tinymce.eachdom.$( 'link[rel="stylesheet"]'editorHead ), function( link ) {
                    if ( 
link.href && link.href.indexOf'skins/lightgray/content.min.css' ) === -&&
                        
link.href.indexOf'skins/wordpress/wp-content.css' ) === -) {

                        
styles += dom.getOuterHTMLlink );
                    }
                } );

                
// Seems the browsers need a bit of time to insert/set the view nodes,
                // or the iframe will fail especially when switching Text => Visual.
                
setTimeout( function() {
                    var 
iframeiframeDocobserveri;

                    
contentNode.innerHTML '';

                    
iframe dom.addcontentNode'iframe', {
                        
/* jshint scripturl: true */
                        
srctinymce.Env.ie 'javascript:""' '',
                        
frameBorder'0',
                        
allowTransparency'true',
                        
scrolling'no',
                        
'class''wpview-sandbox',
                        
style: {
                            
width'100%',
                            
display'block'
                        
}
                    } );

                    
dom.addcontentNode'div', { 'class''wpview-overlay' } );

                    
iframeDoc iframe.contentWindow.document;

                    
iframeDoc.open();

                    
iframeDoc.write(
                        
'<!DOCTYPE html>' +
                        
'<html>' +
                            
'<head>' +
                                
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
                                
head +
                                
styles +
                                
'<style>' +
                                    
'html {' +
                                        
'background: transparent;' +
                                        
'padding: 0;' +
                                        
'margin: 0;' +
                                    
'}' +
                                    
'body#wpview-iframe-sandbox {' +
                                        
'background: transparent;' +
                                        
'padding: 1px 0 !important;' +
                                        
'margin: -1px 0 0 !important;' +
                                    
'}' +
                                    
'body#wpview-iframe-sandbox:before,' +
                                    
'body#wpview-iframe-sandbox:after {' +
                                        
'display: none;' +
                                        
'content: "";' +
                                    
'}' +
                                
'</style>' +
                            
'</head>' +
                            
'<body id="wpview-iframe-sandbox" class="' bodyClasses '">' +
                                
body +
                            
'</body>' +
                        
'</html>'
                    
);

                    
iframeDoc.close();

                    function 
resize() {
                        var 
$iframeiframeDocHeight;

                        
// Make sure the iframe still exists.
                        
if ( iframe.contentWindow ) {
                            
$iframe = $( iframe );
                            
iframeDocHeight = $( iframeDoc.body ).height();

                            if ( 
$iframe.height() !== iframeDocHeight ) {
                                
$iframe.heightiframeDocHeight );
                                
editor.nodeChanged();
                            }
                        }
                    }

                    $( 
iframe.contentWindow ).on'load'resize );

                    if ( 
MutationObserver ) {
                        
observer = new MutationObserver_.debounceresize100 ) );

                        
observer.observeiframeDoc.body, {
                            
attributestrue,
                            
childListtrue,
                            
subtreetrue
                        
} );

                        $( 
node ).one'wp-mce-view-unbind', function() {
                            
observer.disconnect();
                        } );
                    } else {
                        for ( 
16i++ ) {
                            
setTimeoutresize700 );
                        }
                    }

                    function 
classChange() {
                        
iframeDoc.body.className editor.getBody().className;
                    }

                    
editor.on'wp-body-class-change'classChange );

                    $( 
node ).one'wp-mce-view-unbind', function() {
                        
editor.off'wp-body-class-change'classChange );
                    } );

                    
callback && callback.callselfeditornodecontentNode );
                }, 
50 );
            }, 
rendered );
        },

        
/**
         * Sets a loader for all view nodes tied to this view instance.
         */
        
setLoader: function() {
            
this.setContent(
                
'<div class="loading-placeholder">' +
                    
'<div class="dashicons dashicons-admin-media"></div>' +
                    
'<div class="wpview-loading"><ins></ins></div>' +
                
'</div>'
            
);
        },

        
/**
         * Sets an error for all view nodes tied to this view instance.
         *
         * @param {String} message  The error message to set.
         * @param {String} dashicon A dashicon ID (optional). {@link https://developer.wordpress.org/resource/dashicons/}
         */
        
setError: function( messagedashicon ) {
            
this.setContent(
                
'<div class="wpview-error">' +
                    
'<div class="dashicons dashicons-' + ( dashicon || 'no' ) + '"></div>' +
                    
'<p>' message '</p>' +
                
'</div>'
            
);
        },

        
/**
         * Tries to find a text match in a given string.
         *
         * @param {String} content The string to scan.
         *
         * @return {Object}
         */
        
match: function( content ) {
            var 
match wp.shortcode.nextthis.typecontent );

            if ( 
match ) {
                return {
                    
indexmatch.index,
                    
contentmatch.content,
                    
options: {
                        
shortcodematch.shortcode
                    
}
                };
            }
        },

        
/**
         * Update the text of a given view node.
         *
         * @param {String}         text   The new text.
         * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
         * @param {HTMLElement}    node   The view node to update.
         */
        
update: function( texteditornode ) {
            
_.findviews, function( viewtype ) {
                var 
match view.prototype.matchtext );

                if ( 
match ) {
                    $( 
node ).data'rendered'false );
                    
editor.dom.setAttribnode'data-wpview-text'encodeURIComponenttext ) );
                    
wp.mce.views.createInstancetypetextmatch.options ).render();
                    
editor.focus();

                    return 
true;
                }
            } );
        },

        
/**
         * Remove a given view node from the DOM.
         *
         * @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in.
         * @param {HTMLElement}    node   The view node to remove.
         */
        
remove: function( editornode ) {
            
this.unbindNode.callthiseditornode, $( node ).find'.wpview-content' ).get) );
            $( 
node ).trigger'wp-mce-view-unbind' );
            
editor.dom.removenode );
            
editor.focus();
        }
    } );
} )( 
windowwindow.wpwindow.jQuery );

/*
 * The WordPress core TinyMCE views.
 * Views for the gallery, audio, video, playlist and embed shortcodes,
 * and a view for embeddable URLs.
 */
( function( windowviews, $ ) {
    var 
postID = $( '#post_ID' ).val() || 0,
        
mediagalleryavembed;

    
media = {
        
state: [],

        
edit: function( textupdate ) {
            var 
media wp.mediathis.type ],
                
frame media.edittext );

            
this.pausePlayers && this.pausePlayers();

            
_.eachthis.state, function( state ) {
                
frame.statestate ).on'update', function( selection ) {
                    
updatemedia.shortcodeselection ).string() );
                } );
            } );

            
frame.on'close', function() {
                
frame.detach();
            } );

            
frame.open();
        }
    };

    
gallery _.extend( {}, media, {
        
state: [ 'gallery-edit' ],
        
templatewp.media.template'editor-gallery' ),

        
initialize: function() {
            var 
attachments wp.media.gallery.attachmentsthis.shortcodepostID ),
                
attrs this.shortcode.attrs.named,
                
self this;

            
attachments.more()
            .
done( function() {
                
attachments attachments.toJSON();

                
_.eachattachments, function( attachment ) {
                    if ( 
attachment.sizes ) {
                        if ( 
attrs.size && attachment.sizesattrs.size ] ) {
                            
attachment.thumbnail attachment.sizesattrs.size ];
                        } else if ( 
attachment.sizes.thumbnail ) {
                            
attachment.thumbnail attachment.sizes.thumbnail;
                        } else if ( 
attachment.sizes.full ) {
                            
attachment.thumbnail attachment.sizes.full;
                        }
                    }
                } );

                
self.renderself.template( {
                    
attachmentsattachments,
                    
columnsattrs.columns parseIntattrs.columns10 ) : wp.media.galleryDefaults.columns
                
} ) );
            } )
            .
fail( function( jqXHRtextStatus ) {
                
self.setErrortextStatus );
            } );
        }
    } );

    
av _.extend( {}, media, {
        
action'parse-media-shortcode',

        
initialize: function() {
            var 
self this;

            if ( 
this.url ) {
                
this.loader false;
                
this.shortcode wp.media.embed.shortcode( {
                    
urlthis.text
                
} );
            }

            
wp.ajax.postthis.action, {
                
post_IDpostID,
                
typethis.shortcode.tag,
                
shortcodethis.shortcode.string()
            } )
            .
done( function( response ) {
                
self.renderresponse );
            } )
            .
fail( function( response ) {
                if ( 
self.url ) {
                    
self.removeMarkers();
                } else {
                    
self.setErrorresponse.message || response.statusText'admin-media' );
                }
            } );

            
this.getEditors( function( editor ) {
                
editor.on'wpview-selected', function() {
                    
self.pausePlayers();
                } );
            } );
        },

        
pausePlayers: function() {
            
this.getNodes( function( editornodecontent ) {
                var 
win = $( 'iframe.wpview-sandbox'content ).get);

                if ( 
win && ( win win.contentWindow ) && win.mejs ) {
                    
_.eachwin.mejs.players, function( player ) {
                        try {
                            
player.pause();
                        } catch ( 
) {}
                    } );
                }
            } );
        }
    } );

    
embed _.extend( {}, av, {
        
action'parse-embed',

        
edit: function( textupdate ) {
            var 
media wp.media.embed,
                
frame media.edittextthis.url ),
                
self this;

            
this.pausePlayers();

            
frame.state'embed' ).props.on'change:url', function( modelurl ) {
                if ( 
url && model.get'url' ) ) {
                    
frame.state'embed' ).metadata model.toJSON();
                }
            } );

            
frame.state'embed' ).on'select', function() {
                var 
data frame.state'embed' ).metadata;

                if ( 
self.url ) {
                    
updatedata.url );
                } else {
                    
updatemedia.shortcodedata ).string() );
                }
            } );

            
frame.on'close', function() {
                
frame.detach();
            } );

            
frame.open();
        }
    } );

    
views.register'gallery'_.extend( {}, gallery ) );

    
views.register'audio'_.extend( {}, av, {
        
state: [ 'audio-details' ]
    } ) );

    
views.register'video'_.extend( {}, av, {
        
state: [ 'video-details' ]
    } ) );

    
views.register'playlist'_.extend( {}, av, {
        
state: [ 'playlist-edit''video-playlist-edit' ]
    } ) );

    
views.register'embed'_.extend( {}, embed ) );

    
views.register'embedURL'_.extend( {}, embed, {
        
match: function( content ) {
            var 
re = /(^|<p>)(https?://[^s"]+?)(</p>s*|$)/gi,
                
match re.execcontent );

            if ( 
match ) {
                return {
                    
indexmatch.index match[1].length,
                    
contentmatch[2],
                    
options: {
                        
urltrue
                    
}
                };
            }
        }
    } ) );
} )( 
windowwindow.wp.mce.viewswindow.jQuery );
?>
Онлайн: 0
Реклама