[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 // Ensure the global `wp` object exists. 2 window.wp = window.wp || {}; 3 4 (function($){ 5 var views = {}, 6 instances = {}; 7 8 // Create the `wp.mce` object if necessary. 9 wp.mce = wp.mce || {}; 10 11 // wp.mce.view 12 // ----------- 13 // A set of utilities that simplifies adding custom UI within a TinyMCE editor. 14 // At its core, it serves as a series of converters, transforming text to a 15 // custom UI, and back again. 16 wp.mce.view = { 17 // ### defaults 18 defaults: { 19 // The default properties used for objects with the `pattern` key in 20 // `wp.mce.view.add()`. 21 pattern: { 22 view: Backbone.View, 23 text: function( instance ) { 24 return instance.options.original; 25 }, 26 27 toView: function( content ) { 28 if ( ! this.pattern ) 29 return; 30 31 this.pattern.lastIndex = 0; 32 var match = this.pattern.exec( content ); 33 34 if ( ! match ) 35 return; 36 37 return { 38 index: match.index, 39 content: match[0], 40 options: { 41 original: match[0], 42 results: match 43 } 44 }; 45 } 46 }, 47 48 // The default properties used for objects with the `shortcode` key in 49 // `wp.mce.view.add()`. 50 shortcode: { 51 view: Backbone.View, 52 text: function( instance ) { 53 return instance.options.shortcode.string(); 54 }, 55 56 toView: function( content ) { 57 var match = wp.shortcode.next( this.shortcode, content ); 58 59 if ( ! match ) 60 return; 61 62 return { 63 index: match.index, 64 content: match.content, 65 options: { 66 shortcode: match.shortcode 67 } 68 }; 69 } 70 } 71 }, 72 73 // ### add( id, options ) 74 // Registers a new TinyMCE view. 75 // 76 // Accepts a unique `id` and an `options` object. 77 // 78 // `options` accepts the following properties: 79 // 80 // * `pattern` is the regular expression used to scan the content and 81 // detect matching views. 82 // 83 // * `view` is a `Backbone.View` constructor. If a plain object is 84 // provided, it will automatically extend the parent constructor 85 // (usually `Backbone.View`). Views are instantiated when the `pattern` 86 // is successfully matched. The instance's `options` object is provided 87 // with the `original` matched value, the match `results` including 88 // capture groups, and the `viewType`, which is the constructor's `id`. 89 // 90 // * `extend` an existing view by passing in its `id`. The current 91 // view will inherit all properties from the parent view, and if 92 // `view` is set to a plain object, it will extend the parent `view` 93 // constructor. 94 // 95 // * `text` is a method that accepts an instance of the `view` 96 // constructor and transforms it into a text representation. 97 add: function( id, options ) { 98 var parent, remove, base, properties; 99 100 // Fetch the parent view or the default options. 101 if ( options.extend ) 102 parent = wp.mce.view.get( options.extend ); 103 else if ( options.shortcode ) 104 parent = wp.mce.view.defaults.shortcode; 105 else 106 parent = wp.mce.view.defaults.pattern; 107 108 // Extend the `options` object with the parent's properties. 109 _.defaults( options, parent ); 110 options.id = id; 111 112 // Create properties used to enhance the view for use in TinyMCE. 113 properties = { 114 // Ensure the wrapper element and references to the view are 115 // removed. Otherwise, removed views could randomly restore. 116 remove: function() { 117 delete instances[ this.el.id ]; 118 this.$el.parent().remove(); 119 120 // Trigger the inherited `remove` method. 121 if ( remove ) 122 remove.apply( this, arguments ); 123 124 return this; 125 } 126 }; 127 128 // If the `view` provided was an object, use the parent's 129 // `view` constructor as a base. If a `view` constructor 130 // was provided, treat that as the base. 131 if ( _.isFunction( options.view ) ) { 132 base = options.view; 133 } else { 134 base = parent.view; 135 remove = options.view.remove; 136 _.defaults( properties, options.view ); 137 } 138 139 // If there's a `remove` method on the `base` view that wasn't 140 // created by this method, inherit it. 141 if ( ! remove && ! base._mceview ) 142 remove = base.prototype.remove; 143 144 // Automatically create the new `Backbone.View` constructor. 145 options.view = base.extend( properties, { 146 // Flag that the new view has been created by `wp.mce.view`. 147 _mceview: true 148 }); 149 150 views[ id ] = options; 151 }, 152 153 // ### get( id ) 154 // Returns a TinyMCE view options object. 155 get: function( id ) { 156 return views[ id ]; 157 }, 158 159 // ### remove( id ) 160 // Unregisters a TinyMCE view. 161 remove: function( id ) { 162 delete views[ id ]; 163 }, 164 165 // ### toViews( content ) 166 // Scans a `content` string for each view's pattern, replacing any 167 // matches with wrapper elements, and creates a new view instance for 168 // every match. 169 // 170 // To render the views, call `wp.mce.view.render( scope )`. 171 toViews: function( content ) { 172 var pieces = [ { content: content } ], 173 current; 174 175 _.each( views, function( view, viewType ) { 176 current = pieces.slice(); 177 pieces = []; 178 179 _.each( current, function( piece ) { 180 var remaining = piece.content, 181 result; 182 183 // Ignore processed pieces, but retain their location. 184 if ( piece.processed ) { 185 pieces.push( piece ); 186 return; 187 } 188 189 // Iterate through the string progressively matching views 190 // and slicing the string as we go. 191 while ( remaining && (result = view.toView( remaining )) ) { 192 // Any text before the match becomes an unprocessed piece. 193 if ( result.index ) 194 pieces.push({ content: remaining.substring( 0, result.index ) }); 195 196 // Add the processed piece for the match. 197 pieces.push({ 198 content: wp.mce.view.toView( viewType, result.options ), 199 processed: true 200 }); 201 202 // Update the remaining content. 203 remaining = remaining.slice( result.index + result.content.length ); 204 } 205 206 // There are no additional matches. If any content remains, 207 // add it as an unprocessed piece. 208 if ( remaining ) 209 pieces.push({ content: remaining }); 210 }); 211 }); 212 213 return _.pluck( pieces, 'content' ).join(''); 214 }, 215 216 toView: function( viewType, options ) { 217 var view = wp.mce.view.get( viewType ), 218 instance, id; 219 220 if ( ! view ) 221 return ''; 222 223 // Create a new view instance. 224 instance = new view.view( _.extend( options || {}, { 225 viewType: viewType 226 }) ); 227 228 // Use the view's `id` if it already exists. Otherwise, 229 // create a new `id`. 230 id = instance.el.id = instance.el.id || _.uniqueId('__wpmce-'); 231 instances[ id ] = instance; 232 233 // Create a dummy `$wrapper` property to allow `$wrapper` to be 234 // called in the view's `render` method without a conditional. 235 instance.$wrapper = $(); 236 237 return wp.html.string({ 238 // If the view is a span, wrap it in a span. 239 tag: 'span' === instance.tagName ? 'span' : 'div', 240 241 attrs: { 242 'class': 'wp-view-wrap wp-view-type-' + viewType, 243 'data-wp-view': id, 244 'contenteditable': false 245 } 246 }); 247 }, 248 249 // ### render( scope ) 250 // Renders any view instances inside a DOM node `scope`. 251 // 252 // View instances are detected by the presence of wrapper elements. 253 // To generate wrapper elements, pass your content through 254 // `wp.mce.view.toViews( content )`. 255 render: function( scope ) { 256 $( '.wp-view-wrap', scope ).each( function() { 257 var wrapper = $(this), 258 view = wp.mce.view.instance( this ); 259 260 if ( ! view ) 261 return; 262 263 // Link the real wrapper to the view. 264 view.$wrapper = wrapper; 265 // Render the view. 266 view.render(); 267 // Detach the view element to ensure events are not unbound. 268 view.$el.detach(); 269 270 // Empty the wrapper, attach the view element to the wrapper, 271 // and add an ending marker to the wrapper to help regexes 272 // scan the HTML string. 273 wrapper.empty().append( view.el ).append('<span data-wp-view-end class="wp-view-end"></span>'); 274 }); 275 }, 276 277 // ### toText( content ) 278 // Scans an HTML `content` string and replaces any view instances with 279 // their respective text representations. 280 toText: function( content ) { 281 return content.replace( /<(?:div|span)[^>]+data-wp-view="([^"]+)"[^>]*>.*?<span[^>]+data-wp-view-end[^>]*><\/span><\/(?:div|span)>/g, function( match, id ) { 282 var instance = instances[ id ], 283 view; 284 285 if ( instance ) 286 view = wp.mce.view.get( instance.options.viewType ); 287 288 return instance && view ? view.text( instance ) : ''; 289 }); 290 }, 291 292 // ### Remove internal TinyMCE attributes. 293 removeInternalAttrs: function( attrs ) { 294 var result = {}; 295 _.each( attrs, function( value, attr ) { 296 if ( -1 === attr.indexOf('data-mce') ) 297 result[ attr ] = value; 298 }); 299 return result; 300 }, 301 302 // ### Parse an attribute string and removes internal TinyMCE attributes. 303 attrs: function( content ) { 304 return wp.mce.view.removeInternalAttrs( wp.html.attrs( content ) ); 305 }, 306 307 // ### instance( scope ) 308 // 309 // Accepts a MCE view wrapper `node` (i.e. a node with the 310 // `wp-view-wrap` class). 311 instance: function( node ) { 312 var id = $( node ).data('wp-view'); 313 314 if ( id ) 315 return instances[ id ]; 316 }, 317 318 // ### Select a view. 319 // 320 // Accepts a MCE view wrapper `node` (i.e. a node with the 321 // `wp-view-wrap` class). 322 select: function( node ) { 323 var $node = $(node); 324 325 // Bail if node is already selected. 326 if ( $node.hasClass('selected') ) 327 return; 328 329 $node.addClass('selected'); 330 $( node.firstChild ).trigger('select'); 331 }, 332 333 // ### Deselect a view. 334 // 335 // Accepts a MCE view wrapper `node` (i.e. a node with the 336 // `wp-view-wrap` class). 337 deselect: function( node ) { 338 var $node = $(node); 339 340 // Bail if node is already selected. 341 if ( ! $node.hasClass('selected') ) 342 return; 343 344 $node.removeClass('selected'); 345 $( node.firstChild ).trigger('deselect'); 346 } 347 }; 348 349 }(jQuery));
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |