[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/js/ -> shortcode.js (source)

   1  /**
   2   * Utility functions for parsing and handling shortcodes in JavaScript.
   3   *
   4   * @output wp-includes/js/shortcode.js
   5   */
   6  
   7  /**
   8   * Ensure the global `wp` object exists.
   9   *
  10   * @namespace wp
  11   */
  12  window.wp = window.wp || {};
  13  
  14  (function(){
  15      wp.shortcode = {
  16          /*
  17           * ### Find the next matching shortcode.
  18           *
  19           * Given a shortcode `tag`, a block of `text`, and an optional starting
  20           * `index`, returns the next matching shortcode or `undefined`.
  21           *
  22           * Shortcodes are formatted as an object that contains the match
  23           * `content`, the matching `index`, and the parsed `shortcode` object.
  24           */
  25          next: function( tag, text, index ) {
  26              var re = wp.shortcode.regexp( tag ),
  27                  match, result;
  28  
  29              re.lastIndex = index || 0;
  30              match = re.exec( text );
  31  
  32              if ( ! match ) {
  33                  return;
  34              }
  35  
  36              // If we matched an escaped shortcode, try again.
  37              if ( '[' === match[1] && ']' === match[7] ) {
  38                  return wp.shortcode.next( tag, text, re.lastIndex );
  39              }
  40  
  41              result = {
  42                  index:     match.index,
  43                  content:   match[0],
  44                  shortcode: wp.shortcode.fromMatch( match )
  45              };
  46  
  47              // If we matched a leading `[`, strip it from the match
  48              // and increment the index accordingly.
  49              if ( match[1] ) {
  50                  result.content = result.content.slice( 1 );
  51                  result.index++;
  52              }
  53  
  54              // If we matched a trailing `]`, strip it from the match.
  55              if ( match[7] ) {
  56                  result.content = result.content.slice( 0, -1 );
  57              }
  58  
  59              return result;
  60          },
  61  
  62          /*
  63           * ### Replace matching shortcodes in a block of text.
  64           *
  65           * Accepts a shortcode `tag`, content `text` to scan, and a `callback`
  66           * to process the shortcode matches and return a replacement string.
  67           * Returns the `text` with all shortcodes replaced.
  68           *
  69           * Shortcode matches are objects that contain the shortcode `tag`,
  70           * a shortcode `attrs` object, the `content` between shortcode tags,
  71           * and a boolean flag to indicate if the match was a `single` tag.
  72           */
  73          replace: function( tag, text, callback ) {
  74              return text.replace( wp.shortcode.regexp( tag ), function( match, left, tag, attrs, slash, content, closing, right ) {
  75                  // If both extra brackets exist, the shortcode has been
  76                  // properly escaped.
  77                  if ( left === '[' && right === ']' ) {
  78                      return match;
  79                  }
  80  
  81                  // Create the match object and pass it through the callback.
  82                  var result = callback( wp.shortcode.fromMatch( arguments ) );
  83  
  84                  // Make sure to return any of the extra brackets if they
  85                  // weren't used to escape the shortcode.
  86                  return result ? left + result + right : match;
  87              });
  88          },
  89  
  90          /*
  91           * ### Generate a string from shortcode parameters.
  92           *
  93           * Creates a `wp.shortcode` instance and returns a string.
  94           *
  95           * Accepts the same `options` as the `wp.shortcode()` constructor,
  96           * containing a `tag` string, a string or object of `attrs`, a boolean
  97           * indicating whether to format the shortcode using a `single` tag, and a
  98           * `content` string.
  99           */
 100          string: function( options ) {
 101              return new wp.shortcode( options ).string();
 102          },
 103  
 104          /*
 105           * ### Generate a RegExp to identify a shortcode.
 106           *
 107           * The base regex is functionally equivalent to the one found in
 108           * `get_shortcode_regex()` in `wp-includes/shortcodes.php`.
 109           *
 110           * Capture groups:
 111           *
 112           * 1. An extra `[` to allow for escaping shortcodes with double `[[]]`.
 113           * 2. The shortcode name.
 114           * 3. The shortcode argument list.
 115           * 4. The self closing `/`.
 116           * 5. The content of a shortcode when it wraps some content.
 117           * 6. The closing tag.
 118           * 7. An extra `]` to allow for escaping shortcodes with double `[[]]`.
 119           */
 120          regexp: _.memoize( function( tag ) {
 121              return new RegExp( '\\[(\\[?)(' + tag + ')(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)', 'g' );
 122          }),
 123  
 124  
 125          /*
 126           * ### Parse shortcode attributes.
 127           *
 128           * Shortcodes accept many types of attributes. These can chiefly be
 129           * divided into named and numeric attributes:
 130           *
 131           * Named attributes are assigned on a key/value basis, while numeric
 132           * attributes are treated as an array.
 133           *
 134           * Named attributes can be formatted as either `name="value"`,
 135           * `name='value'`, or `name=value`. Numeric attributes can be formatted
 136           * as `"value"` or just `value`.
 137           */
 138          attrs: _.memoize( function( text ) {
 139              var named   = {},
 140                  numeric = [],
 141                  pattern, match;
 142  
 143              /*
 144               * This regular expression is reused from `shortcode_parse_atts()`
 145               * in `wp-includes/shortcodes.php`.
 146               *
 147               * Capture groups:
 148               *
 149               * 1. An attribute name, that corresponds to...
 150               * 2. a value in double quotes.
 151               * 3. An attribute name, that corresponds to...
 152               * 4. a value in single quotes.
 153               * 5. An attribute name, that corresponds to...
 154               * 6. an unquoted value.
 155               * 7. A numeric attribute in double quotes.
 156               * 8. A numeric attribute in single quotes.
 157               * 9. An unquoted numeric attribute.
 158               */
 159              pattern = /([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*'([^']*)'(?:\s|$)|([\w-]+)\s*=\s*([^\s'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|'([^']*)'(?:\s|$)|(\S+)(?:\s|$)/g;
 160  
 161              // Map zero-width spaces to actual spaces.
 162              text = text.replace( /[\u00a0\u200b]/g, ' ' );
 163  
 164              // Match and normalize attributes.
 165              while ( (match = pattern.exec( text )) ) {
 166                  if ( match[1] ) {
 167                      named[ match[1].toLowerCase() ] = match[2];
 168                  } else if ( match[3] ) {
 169                      named[ match[3].toLowerCase() ] = match[4];
 170                  } else if ( match[5] ) {
 171                      named[ match[5].toLowerCase() ] = match[6];
 172                  } else if ( match[7] ) {
 173                      numeric.push( match[7] );
 174                  } else if ( match[8] ) {
 175                      numeric.push( match[8] );
 176                  } else if ( match[9] ) {
 177                      numeric.push( match[9] );
 178                  }
 179              }
 180  
 181              return {
 182                  named:   named,
 183                  numeric: numeric
 184              };
 185          }),
 186  
 187          /*
 188           * ### Generate a Shortcode Object from a RegExp match.
 189           *
 190           * Accepts a `match` object from calling `regexp.exec()` on a `RegExp`
 191           * generated by `wp.shortcode.regexp()`. `match` can also be set
 192           * to the `arguments` from a callback passed to `regexp.replace()`.
 193           */
 194          fromMatch: function( match ) {
 195              var type;
 196  
 197              if ( match[4] ) {
 198                  type = 'self-closing';
 199              } else if ( match[6] ) {
 200                  type = 'closed';
 201              } else {
 202                  type = 'single';
 203              }
 204  
 205              return new wp.shortcode({
 206                  tag:     match[2],
 207                  attrs:   match[3],
 208                  type:    type,
 209                  content: match[5]
 210              });
 211          }
 212      };
 213  
 214  
 215      /*
 216       * Shortcode Objects
 217       * -----------------
 218       *
 219       * Shortcode objects are generated automatically when using the main
 220       * `wp.shortcode` methods: `next()`, `replace()`, and `string()`.
 221       *
 222       * To access a raw representation of a shortcode, pass an `options` object,
 223       * containing a `tag` string, a string or object of `attrs`, a string
 224       * indicating the `type` of the shortcode ('single', 'self-closing',
 225       * or 'closed'), and a `content` string.
 226       */
 227      wp.shortcode = _.extend( function( options ) {
 228          _.extend( this, _.pick( options || {}, 'tag', 'attrs', 'type', 'content' ) );
 229  
 230          var attrs = this.attrs;
 231  
 232          // Ensure we have a correctly formatted `attrs` object.
 233          this.attrs = {
 234              named:   {},
 235              numeric: []
 236          };
 237  
 238          if ( ! attrs ) {
 239              return;
 240          }
 241  
 242          // Parse a string of attributes.
 243          if ( _.isString( attrs ) ) {
 244              this.attrs = wp.shortcode.attrs( attrs );
 245  
 246          // Identify a correctly formatted `attrs` object.
 247          } else if ( _.difference( _.keys( attrs ), [ 'named', 'numeric' ] ).length === 0 ) {
 248              this.attrs = _.defaults( attrs, this.attrs );
 249  
 250          // Handle a flat object of attributes.
 251          } else {
 252              _.each( options.attrs, function( value, key ) {
 253                  this.set( key, value );
 254              }, this );
 255          }
 256      }, wp.shortcode );
 257  
 258      _.extend( wp.shortcode.prototype, {
 259          /*
 260           * ### Get a shortcode attribute.
 261           *
 262           * Automatically detects whether `attr` is named or numeric and routes
 263           * it accordingly.
 264           */
 265          get: function( attr ) {
 266              return this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ];
 267          },
 268  
 269          /*
 270           * ### Set a shortcode attribute.
 271           *
 272           * Automatically detects whether `attr` is named or numeric and routes
 273           * it accordingly.
 274           */
 275          set: function( attr, value ) {
 276              this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ] = value;
 277              return this;
 278          },
 279  
 280          // ### Transform the shortcode match into a string.
 281          string: function() {
 282              var text    = '[' + this.tag;
 283  
 284              _.each( this.attrs.numeric, function( value ) {
 285                  if ( /\s/.test( value ) ) {
 286                      text += ' "' + value + '"';
 287                  } else {
 288                      text += ' ' + value;
 289                  }
 290              });
 291  
 292              _.each( this.attrs.named, function( value, name ) {
 293                  text += ' ' + name + '="' + value + '"';
 294              });
 295  
 296              // If the tag is marked as `single` or `self-closing`, close the
 297              // tag and ignore any additional content.
 298              if ( 'single' === this.type ) {
 299                  return text + ']';
 300              } else if ( 'self-closing' === this.type ) {
 301                  return text + ' /]';
 302              }
 303  
 304              // Complete the opening tag.
 305              text += ']';
 306  
 307              if ( this.content ) {
 308                  text += this.content;
 309              }
 310  
 311              // Add the closing tag.
 312              return text + '[/' + this.tag + ']';
 313          }
 314      });
 315  }());
 316  
 317  /*
 318   * HTML utility functions
 319   * ----------------------
 320   *
 321   * Experimental. These functions may change or be removed in the future.
 322   */
 323  (function(){
 324      wp.html = _.extend( wp.html || {}, {
 325          /*
 326           * ### Parse HTML attributes.
 327           *
 328           * Converts `content` to a set of parsed HTML attributes.
 329           * Utilizes `wp.shortcode.attrs( content )`, which is a valid superset of
 330           * the HTML attribute specification. Reformats the attributes into an
 331           * object that contains the `attrs` with `key:value` mapping, and a record
 332           * of the attributes that were entered using `empty` attribute syntax (i.e.
 333           * with no value).
 334           */
 335          attrs: function( content ) {
 336              var result, attrs;
 337  
 338              // If `content` ends in a slash, strip it.
 339              if ( '/' === content[ content.length - 1 ] ) {
 340                  content = content.slice( 0, -1 );
 341              }
 342  
 343              result = wp.shortcode.attrs( content );
 344              attrs  = result.named;
 345  
 346              _.each( result.numeric, function( key ) {
 347                  if ( /\s/.test( key ) ) {
 348                      return;
 349                  }
 350  
 351                  attrs[ key ] = '';
 352              });
 353  
 354              return attrs;
 355          },
 356  
 357          // ### Convert an HTML-representation of an object to a string.
 358          string: function( options ) {
 359              var text = '<' + options.tag,
 360                  content = options.content || '';
 361  
 362              _.each( options.attrs, function( value, attr ) {
 363                  text += ' ' + attr;
 364  
 365                  // Convert boolean values to strings.
 366                  if ( _.isBoolean( value ) ) {
 367                      value = value ? 'true' : 'false';
 368                  }
 369  
 370                  text += '="' + value + '"';
 371              });
 372  
 373              // Return the result if it is a self-closing tag.
 374              if ( options.single ) {
 375                  return text + ' />';
 376              }
 377  
 378              // Complete the opening tag.
 379              text += '>';
 380  
 381              // If `content` is an object, recursively call this function.
 382              text += _.isObject( content ) ? wp.html.string( content ) : content;
 383  
 384              return text + '</' + options.tag + '>';
 385          }
 386      });
 387  }());


Generated: Tue Apr 23 01:00:02 2024 Cross-referenced by PHPXref 0.7.1