* @output wp-includes/js/wp-api.js
(function( window, undefined ) {
/** @namespace wp.api.models */
/** @namespace wp.api.collections */
/** @namespace wp.api.views */
window.wp = window.wp || {};
wp.api = wp.api || new WP_API();
wp.api.versionString = wp.api.versionString || 'wp/v2/';
// Alias _includes to _.contains, ensuring it is available if lodash is used.
if ( ! _.isFunction( _.includes ) && _.isFunction( _.contains ) ) {
(function( window, undefined ) {
window.wp = window.wp || {};
/** @namespace wp.api.utils */
wp.api.utils = wp.api.utils || {};
* Determine model based on API route.
* @param {string} route The API route.
* @return {Backbone Model} The model found at given route. Undefined if not found.
wp.api.getModelByRoute = function( route ) {
return _.find( wp.api.models, function( model ) {
return model.prototype.route && route === model.prototype.route.index;
* Determine collection based on API route.
* @param {string} route The API route.
* @return {Backbone Model} The collection found at given route. Undefined if not found.
wp.api.getCollectionByRoute = function( route ) {
return _.find( wp.api.collections, function( collection ) {
return collection.prototype.route && route === collection.prototype.route.index;
* ECMAScript 5 shim, adapted from MDN.
* @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
if ( ! Date.prototype.toISOString ) {
pad = function( number ) {
Date.prototype.toISOString = function() {
return this.getUTCFullYear() +
'-' + pad( this.getUTCMonth() + 1 ) +
'-' + pad( this.getUTCDate() ) +
'T' + pad( this.getUTCHours() ) +
':' + pad( this.getUTCMinutes() ) +
':' + pad( this.getUTCSeconds() ) +
'.' + String( ( this.getUTCMilliseconds() / 1000 ).toFixed( 3 ) ).slice( 2, 5 ) +
* Parse date into ISO8601 format.
wp.api.utils.parseISO8601 = function( date ) {
var timestamp, struct, i, k,
numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
* ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
* before falling back to any implementation-specific date parsing, so that’s what we do, even if native
* implementations could be faster.
// 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
if ( ( struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec( date ) ) ) {
// Avoid NaN timestamps caused by “undefined” values being passed to Date.UTC.
for ( i = 0; ( k = numericKeys[i] ); ++i ) {
struct[k] = +struct[k] || 0;
// Allow undefined days and months.
struct[2] = ( +struct[2] || 1 ) - 1;
struct[3] = +struct[3] || 1;
if ( 'Z' !== struct[8] && undefined !== struct[9] ) {
minutesOffset = struct[10] * 60 + struct[11];
if ( '+' === struct[9] ) {
minutesOffset = 0 - minutesOffset;
timestamp = Date.UTC( struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7] );
timestamp = Date.parse ? Date.parse( date ) : NaN;
* Helper function for getting the root URL.
* @return {[type]} [description]
wp.api.utils.getRootUrl = function() {
return window.location.origin ?
window.location.origin + '/' :
window.location.protocol + '//' + window.location.host + '/';
* Helper for capitalizing strings.
wp.api.utils.capitalize = function( str ) {
if ( _.isUndefined( str ) ) {
return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
* Helper function that capitalizes the first word and camel cases any words starting
* after dashes, removing the dashes.
wp.api.utils.capitalizeAndCamelCaseDashes = function( str ) {
if ( _.isUndefined( str ) ) {
str = wp.api.utils.capitalize( str );
return wp.api.utils.camelCaseDashes( str );
* Helper function to camel case the letter after dashes, removing the dashes.
wp.api.utils.camelCaseDashes = function( str ) {
return str.replace( /-([a-z])/g, function( g ) {
return g[ 1 ].toUpperCase();
* Extract a route part based on negative index.
* @param {string} route The endpoint route.
* @param {number} part The number of parts from the end of the route to retrieve. Default 1.
* Example route `/a/b/c`: part 1 is `c`, part 2 is `b`, part 3 is `a`.
* @param {string} [versionString] Version string, defaults to `wp.api.versionString`.
* @param {boolean} [reverse] Whether to reverse the order when extracting the route part. Optional, default false.
wp.api.utils.extractRoutePart = function( route, part, versionString, reverse ) {
versionString = versionString || wp.api.versionString;
// Remove versions string from route to avoid returning it.
if ( 0 === route.indexOf( '/' + versionString ) ) {
route = route.substr( versionString.length + 1 );
routeParts = route.split( '/' );
routeParts = routeParts.reverse();
if ( _.isUndefined( routeParts[ --part ] ) ) {
return routeParts[ part ];
* Extract a parent name from a passed route.
* @param {string} route The route to extract a name from.
wp.api.utils.extractParentName = function( route ) {
lastSlash = route.lastIndexOf( '_id>[\\d]+)/' );
name = route.substr( 0, lastSlash - 1 );
name = name.split( '/' );
* Add args and options to a model prototype from a route's endpoints.
* @param {Array} routeEndpoints Array of route endpoints.
* @param {Object} modelInstance An instance of the model (or collection)
wp.api.utils.decorateFromRoute = function( routeEndpoints, modelInstance ) {
* Build the args based on route endpoint data.
_.each( routeEndpoints, function( routeEndpoint ) {
// Add post and edit endpoints as model args.
if ( _.includes( routeEndpoint.methods, 'POST' ) || _.includes( routeEndpoint.methods, 'PUT' ) ) {
// Add any non-empty args, merging them into the args object.
if ( ! _.isEmpty( routeEndpoint.args ) ) {
// Set as default if no args yet.
if ( _.isEmpty( modelInstance.prototype.args ) ) {
modelInstance.prototype.args = routeEndpoint.args;
// We already have args, merge these new args in.
modelInstance.prototype.args = _.extend( modelInstance.prototype.args, routeEndpoint.args );
// Add GET method as model options.
if ( _.includes( routeEndpoint.methods, 'GET' ) ) {
// Add any non-empty args, merging them into the defaults object.
if ( ! _.isEmpty( routeEndpoint.args ) ) {
// Set as default if no defaults yet.
if ( _.isEmpty( modelInstance.prototype.options ) ) {
modelInstance.prototype.options = routeEndpoint.args;
// We already have options, merge these new args in.
modelInstance.prototype.options = _.extend( modelInstance.prototype.options, routeEndpoint.args );
* Add mixins and helpers to models depending on their defaults.
* @param {Backbone Model} model The model to attach helpers and mixins to.
* @param {string} modelClassName The classname of the constructed model.
* @param {Object} loadingObjects An object containing the models and collections we are building.
wp.api.utils.addMixinsAndHelpers = function( model, modelClassName, loadingObjects ) {
* Array of parseable dates.
parseableDates = [ 'date', 'modified', 'date_gmt', 'modified_gmt' ],
* Mixin for all content that is time stamped.
* This mixin converts between mysql timestamps and JavaScript Dates when syncing a model
* to or from the server. For example, a date stored as `2015-12-27T21:22:24` on the server
* gets expanded to `Sun Dec 27 2015 14:22:24 GMT-0700 (MST)` when the model is fetched.
* @type {{toJSON: toJSON, parse: parse}}.
* Prepare a JavaScript Date for transmitting to the server.
* This helper function accepts a field and Date object. It converts the passed Date
* to an ISO string and sets that on the model field.
* @param {Date} date A JavaScript date object. WordPress expects dates in UTC.
* @param {string} field The date field to set. One of 'date', 'date_gmt', 'date_modified'
* or 'date_modified_gmt'. Optional, defaults to 'date'.
setDate: function( date, field ) {
var theField = field || 'date';
// Don't alter non-parsable date fields.
if ( _.indexOf( parseableDates, theField ) < 0 ) {
this.set( theField, date.toISOString() );
* Get a JavaScript Date from the passed field.
* WordPress returns 'date' and 'date_modified' in the timezone of the server as well as
* UTC dates as 'date_gmt' and 'date_modified_gmt'. Draft posts do not include UTC dates.
* @param {string} field The date field to set. One of 'date', 'date_gmt', 'date_modified'
* or 'date_modified_gmt'. Optional, defaults to 'date'.
getDate: function( field ) {
var theField = field || 'date',
theISODate = this.get( theField );
// Only get date fields and non-null values.
if ( _.indexOf( parseableDates, theField ) < 0 || _.isNull( theISODate ) ) {
return new Date( wp.api.utils.parseISO8601( theISODate ) );
* Build a helper function to retrieve related model.
* @param {string} parentModel The parent model.
* @param {number} modelId The model ID if the object to request
* @param {string} modelName The model name to use when constructing the model.
* @param {string} embedSourcePoint Where to check the embedded object for _embed data.
* @param {string} embedCheckField Which model field to check to see if the model has data.
* @return {Deferred.promise} A promise which resolves to the constructed model.
buildModelGetter = function( parentModel, modelId, modelName, embedSourcePoint, embedCheckField ) {
var getModel, embeddedObjects, attributes, deferred;
deferred = jQuery.Deferred();
embeddedObjects = parentModel.get( '_embedded' ) || {};
// Verify that we have a valid object id.
if ( ! _.isNumber( modelId ) || 0 === modelId ) {
// If we have embedded object data, use that when constructing the getModel.
if ( embeddedObjects[ embedSourcePoint ] ) {
attributes = _.findWhere( embeddedObjects[ embedSourcePoint ], { id: modelId } );
// Otherwise use the modelId.
attributes = { id: modelId };
// Create the new getModel model.
getModel = new wp.api.models[ modelName ]( attributes );
if ( ! getModel.get( embedCheckField ) ) {
success: function( getModel ) {
deferred.resolve( getModel );
error: function( getModel, response ) {
deferred.reject( response );
// Resolve with the embedded model.
deferred.resolve( getModel );
return deferred.promise();
* Build a helper to retrieve a collection.
* @param {string} parentModel The parent model.
* @param {string} collectionName The name to use when constructing the collection.
* @param {string} embedSourcePoint Where to check the embedded object for _embed data.
* @param {string} embedIndex An additional optional index for the _embed data.
* @return {Deferred.promise} A promise which resolves to the constructed collection.
buildCollectionGetter = function( parentModel, collectionName, embedSourcePoint, embedIndex ) {
* Returns a promise that resolves to the requested collection
* Uses the embedded data if available, otherwise fetches the
* @return {Deferred.promise} promise Resolves to a wp.api.collections[ collectionName ]
var postId, embeddedObjects, getObjects,
deferred = jQuery.Deferred();
postId = parentModel.get( 'id' );
embeddedObjects = parentModel.get( '_embedded' ) || {};
// Verify that we have a valid post ID.
if ( ! _.isNumber( postId ) || 0 === postId ) {
// If we have embedded getObjects data, use that when constructing the getObjects.
if ( ! _.isUndefined( embedSourcePoint ) && ! _.isUndefined( embeddedObjects[ embedSourcePoint ] ) ) {
// Some embeds also include an index offset, check for that.
if ( _.isUndefined( embedIndex ) ) {
// Use the embed source point directly.
properties = embeddedObjects[ embedSourcePoint ];
// Add the index to the embed source point.
properties = embeddedObjects[ embedSourcePoint ][ embedIndex ];
// Otherwise use the postId.
classProperties = { parent: postId };
// Create the new getObjects collection.
getObjects = new wp.api.collections[ collectionName ]( properties, classProperties );
// If we didn’t have embedded getObjects, fetch the getObjects data.
if ( _.isUndefined( getObjects.models[0] ) ) {
success: function( getObjects ) {
// Add a helper 'parent_post' attribute onto the model.
setHelperParentPost( getObjects, postId );
deferred.resolve( getObjects );
error: function( getModel, response ) {
deferred.reject( response );
// Add a helper 'parent_post' attribute onto the model.
setHelperParentPost( getObjects, postId );
deferred.resolve( getObjects );
return deferred.promise();
* Set the model post parent.
setHelperParentPost = function( collection, postId ) {
// Attach post_parent id to the collection.