PageRenderTime 42ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/files/wordpress/3.5.1/js/shortcode.js

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