wp/wp-includes/js/shortcode.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    11  */
    11  */
    12 window.wp = window.wp || {};
    12 window.wp = window.wp || {};
    13 
    13 
    14 (function(){
    14 (function(){
    15 	wp.shortcode = {
    15 	wp.shortcode = {
    16 		// ### Find the next matching shortcode
    16 		/*
    17 		//
    17 		 * ### Find the next matching shortcode.
    18 		// Given a shortcode `tag`, a block of `text`, and an optional starting
    18 		 *
    19 		// `index`, returns the next matching shortcode or `undefined`.
    19 		 * Given a shortcode `tag`, a block of `text`, and an optional starting
    20 		//
    20 		 * `index`, returns the next matching shortcode or `undefined`.
    21 		// Shortcodes are formatted as an object that contains the match
    21 		 *
    22 		// `content`, the matching `index`, and the parsed `shortcode` object.
    22 		 * Shortcodes are formatted as an object that contains the match
       
    23 		 * `content`, the matching `index`, and the parsed `shortcode` object.
       
    24 		 */
    23 		next: function( tag, text, index ) {
    25 		next: function( tag, text, index ) {
    24 			var re = wp.shortcode.regexp( tag ),
    26 			var re = wp.shortcode.regexp( tag ),
    25 				match, result;
    27 				match, result;
    26 
    28 
    27 			re.lastIndex = index || 0;
    29 			re.lastIndex = index || 0;
    55 			}
    57 			}
    56 
    58 
    57 			return result;
    59 			return result;
    58 		},
    60 		},
    59 
    61 
    60 		// ### Replace matching shortcodes in a block of text
    62 		/*
    61 		//
    63 		 * ### Replace matching shortcodes in a block of text.
    62 		// Accepts a shortcode `tag`, content `text` to scan, and a `callback`
    64 		 *
    63 		// to process the shortcode matches and return a replacement string.
    65 		 * Accepts a shortcode `tag`, content `text` to scan, and a `callback`
    64 		// Returns the `text` with all shortcodes replaced.
    66 		 * to process the shortcode matches and return a replacement string.
    65 		//
    67 		 * Returns the `text` with all shortcodes replaced.
    66 		// Shortcode matches are objects that contain the shortcode `tag`,
    68 		 *
    67 		// a shortcode `attrs` object, the `content` between shortcode tags,
    69 		 * Shortcode matches are objects that contain the shortcode `tag`,
    68 		// and a boolean flag to indicate if the match was a `single` 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 		 */
    69 		replace: function( tag, text, callback ) {
    73 		replace: function( tag, text, callback ) {
    70 			return text.replace( wp.shortcode.regexp( tag ), function( match, left, tag, attrs, slash, content, closing, right ) {
    74 			return text.replace( wp.shortcode.regexp( tag ), function( match, left, tag, attrs, slash, content, closing, right ) {
    71 				// If both extra brackets exist, the shortcode has been
    75 				// If both extra brackets exist, the shortcode has been
    72 				// properly escaped.
    76 				// properly escaped.
    73 				if ( left === '[' && right === ']' ) {
    77 				if ( left === '[' && right === ']' ) {
    81 				// weren't used to escape the shortcode.
    85 				// weren't used to escape the shortcode.
    82 				return result ? left + result + right : match;
    86 				return result ? left + result + right : match;
    83 			});
    87 			});
    84 		},
    88 		},
    85 
    89 
    86 		// ### Generate a string from shortcode parameters
    90 		/*
    87 		//
    91 		 * ### Generate a string from shortcode parameters.
    88 		// Creates a `wp.shortcode` instance and returns a string.
    92 		 *
    89 		//
    93 		 * Creates a `wp.shortcode` instance and returns a string.
    90 		// Accepts the same `options` as the `wp.shortcode()` constructor,
    94 		 *
    91 		// containing a `tag` string, a string or object of `attrs`, a boolean
    95 		 * Accepts the same `options` as the `wp.shortcode()` constructor,
    92 		// indicating whether to format the shortcode using a `single` tag, and a
    96 		 * containing a `tag` string, a string or object of `attrs`, a boolean
    93 		// `content` string.
    97 		 * indicating whether to format the shortcode using a `single` tag, and a
       
    98 		 * `content` string.
       
    99 		 */
    94 		string: function( options ) {
   100 		string: function( options ) {
    95 			return new wp.shortcode( options ).string();
   101 			return new wp.shortcode( options ).string();
    96 		},
   102 		},
    97 
   103 
    98 		// ### Generate a RegExp to identify a shortcode
   104 		/*
    99 		//
   105 		 * ### Generate a RegExp to identify a shortcode.
   100 		// The base regex is functionally equivalent to the one found in
   106 		 *
   101 		// `get_shortcode_regex()` in `wp-includes/shortcodes.php`.
   107 		 * The base regex is functionally equivalent to the one found in
   102 		//
   108 		 * `get_shortcode_regex()` in `wp-includes/shortcodes.php`.
   103 		// Capture groups:
   109 		 *
   104 		//
   110 		 * Capture groups:
   105 		// 1. An extra `[` to allow for escaping shortcodes with double `[[]]`
   111 		 *
   106 		// 2. The shortcode name
   112 		 * 1. An extra `[` to allow for escaping shortcodes with double `[[]]`.
   107 		// 3. The shortcode argument list
   113 		 * 2. The shortcode name.
   108 		// 4. The self closing `/`
   114 		 * 3. The shortcode argument list.
   109 		// 5. The content of a shortcode when it wraps some content.
   115 		 * 4. The self closing `/`.
   110 		// 6. The closing tag.
   116 		 * 5. The content of a shortcode when it wraps some content.
   111 		// 7. An extra `]` to allow for escaping shortcodes with double `[[]]`
   117 		 * 6. The closing tag.
       
   118 		 * 7. An extra `]` to allow for escaping shortcodes with double `[[]]`.
       
   119 		 */
   112 		regexp: _.memoize( function( tag ) {
   120 		regexp: _.memoize( function( tag ) {
   113 			return new RegExp( '\\[(\\[?)(' + tag + ')(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)', 'g' );
   121 			return new RegExp( '\\[(\\[?)(' + tag + ')(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)', 'g' );
   114 		}),
   122 		}),
   115 
   123 
   116 
   124 
   117 		// ### Parse shortcode attributes
   125 		/*
   118 		//
   126 		 * ### Parse shortcode attributes.
   119 		// Shortcodes accept many types of attributes. These can chiefly be
   127 		 *
   120 		// divided into named and numeric attributes:
   128 		 * Shortcodes accept many types of attributes. These can chiefly be
   121 		//
   129 		 * divided into named and numeric attributes:
   122 		// Named attributes are assigned on a key/value basis, while numeric
   130 		 *
   123 		// attributes are treated as an array.
   131 		 * Named attributes are assigned on a key/value basis, while numeric
   124 		//
   132 		 * attributes are treated as an array.
   125 		// Named attributes can be formatted as either `name="value"`,
   133 		 *
   126 		// `name='value'`, or `name=value`. Numeric attributes can be formatted
   134 		 * Named attributes can be formatted as either `name="value"`,
   127 		// as `"value"` or just `value`.
   135 		 * `name='value'`, or `name=value`. Numeric attributes can be formatted
       
   136 		 * as `"value"` or just `value`.
       
   137 		 */
   128 		attrs: _.memoize( function( text ) {
   138 		attrs: _.memoize( function( text ) {
   129 			var named   = {},
   139 			var named   = {},
   130 				numeric = [],
   140 				numeric = [],
   131 				pattern, match;
   141 				pattern, match;
   132 
   142 
   133 			// This regular expression is reused from `shortcode_parse_atts()`
   143 			/*
   134 			// in `wp-includes/shortcodes.php`.
   144 			 * This regular expression is reused from `shortcode_parse_atts()`
   135 			//
   145 			 * in `wp-includes/shortcodes.php`.
   136 			// Capture groups:
   146 			 *
   137 			//
   147 			 * Capture groups:
   138 			// 1. An attribute name, that corresponds to...
   148 			 *
   139 			// 2. a value in double quotes.
   149 			 * 1. An attribute name, that corresponds to...
   140 			// 3. An attribute name, that corresponds to...
   150 			 * 2. a value in double quotes.
   141 			// 4. a value in single quotes.
   151 			 * 3. An attribute name, that corresponds to...
   142 			// 5. An attribute name, that corresponds to...
   152 			 * 4. a value in single quotes.
   143 			// 6. an unquoted value.
   153 			 * 5. An attribute name, that corresponds to...
   144 			// 7. A numeric attribute in double quotes.
   154 			 * 6. an unquoted value.
   145 			// 8. A numeric attribute in single quotes.
   155 			 * 7. A numeric attribute in double quotes.
   146 			// 9. An unquoted numeric attribute.
   156 			 * 8. A numeric attribute in single quotes.
       
   157 			 * 9. An unquoted numeric attribute.
       
   158 			 */
   147 			pattern = /([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*'([^']*)'(?:\s|$)|([\w-]+)\s*=\s*([^\s'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|'([^']*)'(?:\s|$)|(\S+)(?:\s|$)/g;
   159 			pattern = /([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*'([^']*)'(?:\s|$)|([\w-]+)\s*=\s*([^\s'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|'([^']*)'(?:\s|$)|(\S+)(?:\s|$)/g;
   148 
   160 
   149 			// Map zero-width spaces to actual spaces.
   161 			// Map zero-width spaces to actual spaces.
   150 			text = text.replace( /[\u00a0\u200b]/g, ' ' );
   162 			text = text.replace( /[\u00a0\u200b]/g, ' ' );
   151 
   163 
   170 				named:   named,
   182 				named:   named,
   171 				numeric: numeric
   183 				numeric: numeric
   172 			};
   184 			};
   173 		}),
   185 		}),
   174 
   186 
   175 		// ### Generate a Shortcode Object from a RegExp match
   187 		/*
   176 		// Accepts a `match` object from calling `regexp.exec()` on a `RegExp`
   188 		 * ### Generate a Shortcode Object from a RegExp match.
   177 		// generated by `wp.shortcode.regexp()`. `match` can also be set to the
   189 		 *
   178 		// `arguments` from a callback passed to `regexp.replace()`.
   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 		 */
   179 		fromMatch: function( match ) {
   194 		fromMatch: function( match ) {
   180 			var type;
   195 			var type;
   181 
   196 
   182 			if ( match[4] ) {
   197 			if ( match[4] ) {
   183 				type = 'self-closing';
   198 				type = 'self-closing';
   195 			});
   210 			});
   196 		}
   211 		}
   197 	};
   212 	};
   198 
   213 
   199 
   214 
   200 	// Shortcode Objects
   215 	/*
   201 	// -----------------
   216 	 * Shortcode Objects
   202 	//
   217 	 * -----------------
   203 	// Shortcode objects are generated automatically when using the main
   218 	 *
   204 	// `wp.shortcode` methods: `next()`, `replace()`, and `string()`.
   219 	 * Shortcode objects are generated automatically when using the main
   205 	//
   220 	 * `wp.shortcode` methods: `next()`, `replace()`, and `string()`.
   206 	// To access a raw representation of a shortcode, pass an `options` object,
   221 	 *
   207 	// containing a `tag` string, a string or object of `attrs`, a string
   222 	 * To access a raw representation of a shortcode, pass an `options` object,
   208 	// indicating the `type` of the shortcode ('single', 'self-closing', or
   223 	 * containing a `tag` string, a string or object of `attrs`, a string
   209 	// 'closed'), and a `content` string.
   224 	 * indicating the `type` of the shortcode ('single', 'self-closing',
       
   225 	 * or 'closed'), and a `content` string.
       
   226 	 */
   210 	wp.shortcode = _.extend( function( options ) {
   227 	wp.shortcode = _.extend( function( options ) {
   211 		_.extend( this, _.pick( options || {}, 'tag', 'attrs', 'type', 'content' ) );
   228 		_.extend( this, _.pick( options || {}, 'tag', 'attrs', 'type', 'content' ) );
   212 
   229 
   213 		var attrs = this.attrs;
   230 		var attrs = this.attrs;
   214 
   231 
   225 		// Parse a string of attributes.
   242 		// Parse a string of attributes.
   226 		if ( _.isString( attrs ) ) {
   243 		if ( _.isString( attrs ) ) {
   227 			this.attrs = wp.shortcode.attrs( attrs );
   244 			this.attrs = wp.shortcode.attrs( attrs );
   228 
   245 
   229 		// Identify a correctly formatted `attrs` object.
   246 		// Identify a correctly formatted `attrs` object.
   230 		} else if ( _.isEqual( _.keys( attrs ), [ 'named', 'numeric' ] ) ) {
   247 		} else if ( _.difference( _.keys( attrs ), [ 'named', 'numeric' ] ).length === 0 ) {
   231 			this.attrs = attrs;
   248 			this.attrs = _.defaults( attrs, this.attrs );
   232 
   249 
   233 		// Handle a flat object of attributes.
   250 		// Handle a flat object of attributes.
   234 		} else {
   251 		} else {
   235 			_.each( options.attrs, function( value, key ) {
   252 			_.each( options.attrs, function( value, key ) {
   236 				this.set( key, value );
   253 				this.set( key, value );
   237 			}, this );
   254 			}, this );
   238 		}
   255 		}
   239 	}, wp.shortcode );
   256 	}, wp.shortcode );
   240 
   257 
   241 	_.extend( wp.shortcode.prototype, {
   258 	_.extend( wp.shortcode.prototype, {
   242 		// ### Get a shortcode attribute
   259 		/*
   243 		//
   260 		 * ### Get a shortcode attribute.
   244 		// Automatically detects whether `attr` is named or numeric and routes
   261 		 *
   245 		// it accordingly.
   262 		 * Automatically detects whether `attr` is named or numeric and routes
       
   263 		 * it accordingly.
       
   264 		 */
   246 		get: function( attr ) {
   265 		get: function( attr ) {
   247 			return this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ];
   266 			return this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ];
   248 		},
   267 		},
   249 
   268 
   250 		// ### Set a shortcode attribute
   269 		/*
   251 		//
   270 		 * ### Set a shortcode attribute.
   252 		// Automatically detects whether `attr` is named or numeric and routes
   271 		 *
   253 		// it accordingly.
   272 		 * Automatically detects whether `attr` is named or numeric and routes
       
   273 		 * it accordingly.
       
   274 		 */
   254 		set: function( attr, value ) {
   275 		set: function( attr, value ) {
   255 			this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ] = value;
   276 			this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ] = value;
   256 			return this;
   277 			return this;
   257 		},
   278 		},
   258 
   279 
   259 		// ### Transform the shortcode match into a string
   280 		// ### Transform the shortcode match into a string.
   260 		string: function() {
   281 		string: function() {
   261 			var text    = '[' + this.tag;
   282 			var text    = '[' + this.tag;
   262 
   283 
   263 			_.each( this.attrs.numeric, function( value ) {
   284 			_.each( this.attrs.numeric, function( value ) {
   264 				if ( /\s/.test( value ) ) {
   285 				if ( /\s/.test( value ) ) {
   291 			return text + '[/' + this.tag + ']';
   312 			return text + '[/' + this.tag + ']';
   292 		}
   313 		}
   293 	});
   314 	});
   294 }());
   315 }());
   295 
   316 
   296 // HTML utility functions
   317 /*
   297 // ----------------------
   318  * HTML utility functions
   298 //
   319  * ----------------------
   299 // Experimental. These functions may change or be removed in the future.
   320  *
       
   321  * Experimental. These functions may change or be removed in the future.
       
   322  */
   300 (function(){
   323 (function(){
   301 	wp.html = _.extend( wp.html || {}, {
   324 	wp.html = _.extend( wp.html || {}, {
   302 		// ### Parse HTML attributes.
   325 		/*
   303 		//
   326 		 * ### Parse HTML attributes.
   304 		// Converts `content` to a set of parsed HTML attributes.
   327 		 *
   305 		// Utilizes `wp.shortcode.attrs( content )`, which is a valid superset of
   328 		 * Converts `content` to a set of parsed HTML attributes.
   306 		// the HTML attribute specification. Reformats the attributes into an
   329 		 * Utilizes `wp.shortcode.attrs( content )`, which is a valid superset of
   307 		// object that contains the `attrs` with `key:value` mapping, and a record
   330 		 * the HTML attribute specification. Reformats the attributes into an
   308 		// of the attributes that were entered using `empty` attribute syntax (i.e.
   331 		 * object that contains the `attrs` with `key:value` mapping, and a record
   309 		// with no value).
   332 		 * of the attributes that were entered using `empty` attribute syntax (i.e.
       
   333 		 * with no value).
       
   334 		 */
   310 		attrs: function( content ) {
   335 		attrs: function( content ) {
   311 			var result, attrs;
   336 			var result, attrs;
   312 
   337 
   313 			// If `content` ends in a slash, strip it.
   338 			// If `content` ends in a slash, strip it.
   314 			if ( '/' === content[ content.length - 1 ] ) {
   339 			if ( '/' === content[ content.length - 1 ] ) {