: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( _.isFunction( model.nonce ) && ! _.isEmpty( model.nonce() ) ) {
beforeSend = options.beforeSend;
// Include the nonce with requests.
options.beforeSend = function( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', model.nonce() );
return beforeSend.apply( self, arguments );
// Update the nonce when a new nonce is returned with the response.
options.complete = function( xhr ) {
var returnedNonce = xhr.getResponseHeader( 'X-WP-Nonce' );
if ( returnedNonce && _.isFunction( model.nonce ) && model.nonce() !== returnedNonce ) {
model.endpointModel.set( 'nonce', returnedNonce );
// When reading, add pagination data.
if ( 'read' === method ) {
self.state.data = _.clone( options.data );
delete self.state.data.page;
self.state.data = options.data = {};
if ( 'undefined' === typeof options.data.page ) {
self.state.currentPage = null;
self.state.totalPages = null;
self.state.totalObjects = null;
self.state.currentPage = options.data.page - 1;
success = options.success;
options.success = function( data, textStatus, request ) {
if ( ! _.isUndefined( request ) ) {
self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 );
self.state.totalObjects = parseInt( request.getResponseHeader( 'x-wp-total' ), 10 );
if ( null === self.state.currentPage ) {
self.state.currentPage = 1;
self.state.currentPage++;
return success.apply( this, arguments );
// Continue by calling Backbone's sync.
return Backbone.sync( method, model, options );
* Fetches the next page of objects if a new page exists.
* @param {data: {page}} options.
more: function( options ) {
options.data = options.data || {};
_.extend( options.data, this.state.data );
if ( 'undefined' === typeof options.data.page ) {
if ( ! this.hasMore() ) {
if ( null === this.state.currentPage || this.state.currentPage <= 1 ) {
options.data.page = this.state.currentPage + 1;
return this.fetch( options );
* Returns true if there are more pages of objects available.
if ( null === this.state.totalPages ||
null === this.state.totalObjects ||
null === this.state.currentPage ) {
return ( this.state.currentPage < this.state.totalPages );
var Endpoint, initializedDeferreds = {},
wpApiSettings = window.wpApiSettings || {};
window.wp = window.wp || {};
// If wpApiSettings is unavailable, try the default.
if ( _.isEmpty( wpApiSettings ) ) {
wpApiSettings.root = window.location.origin + '/wp-json/';
Endpoint = Backbone.Model.extend(/** @lends Endpoint.prototype */{
apiRoot: wpApiSettings.root,
versionString: wp.api.versionString,
* Initialize the Endpoint model.
var model = this, deferred;
Backbone.Model.prototype.initialize.apply( model, arguments );
deferred = jQuery.Deferred();
model.schemaConstructed = deferred.promise();
model.schemaModel = new wp.api.models.Schema( null, {
apiRoot: model.get( 'apiRoot' ),
versionString: model.get( 'versionString' ),
nonce: model.get( 'nonce' )
// When the model loads, resolve the promise.
model.schemaModel.once( 'change', function() {
model.constructFromSchema();
deferred.resolve( model );
if ( model.get( 'schema' ) ) {
// Use schema supplied as model attribute.
model.schemaModel.set( model.schemaModel.parse( model.get( 'schema' ) ) );
! _.isUndefined( sessionStorage ) &&
( _.isUndefined( wpApiSettings.cacheSchema ) || wpApiSettings.cacheSchema ) &&
sessionStorage.getItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ) )
// Used a cached copy of the schema model if available.
model.schemaModel.set( model.schemaModel.parse( JSON.parse( sessionStorage.getItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ) ) ) ) );
model.schemaModel.fetch( {
* When the server returns the schema model data, store the data in a sessionCache so we don't
* have to retrieve it again for this session. Then, construct the models and collections based
* on the schema model data.
success: function( newSchemaModel ) {
// Store a copy of the schema model in the session cache if available.
if ( ! _.isUndefined( sessionStorage ) && ( _.isUndefined( wpApiSettings.cacheSchema ) || wpApiSettings.cacheSchema ) ) {
sessionStorage.setItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ), JSON.stringify( newSchemaModel ) );
// Fail silently, fixes errors in safari private mode.
// Log the error condition.
window.console.log( err );
constructFromSchema: function() {
var routeModel = this, modelRoutes, collectionRoutes, schemaRoot, loadingObjects,
* Set up the model and collection name mapping options. As the schema is built, the
* model and collection names will be adjusted if they are found in the mapping object.
* Localizing a variable wpApiSettings.mapping will over-ride the default mapping options.
mapping = wpApiSettings.mapping || {
'Categories': 'Category',
'PagesRevisions': 'PageRevision',
'PostsCategories': 'PostCategory',
'PostsRevisions': 'PostRevision',
'Taxonomies': 'Taxonomy',
'PagesRevisions': 'PageRevisions',
'PostsCategories': 'PostCategories',
'PostsRevisions': 'PostRevisions',
modelEndpoints = routeModel.get( 'modelEndpoints' ),
modelRegex = new RegExp( '(?:.*[+)]|\/(' + modelEndpoints.join( '|' ) + '))$' );
* Iterate thru the routes, picking up models and collections to build. Builds two arrays,
* one for models and one for collections.
schemaRoot = routeModel.get( 'apiRoot' ).replace( wp.api.utils.getRootUrl(), '' );
* Tracking objects for models and collections.
loadingObjects.models = {};
loadingObjects.collections = {};
_.each( routeModel.schemaModel.get( 'routes' ), function( route, index ) {
// Skip the schema root if included in the schema.
if ( index !== routeModel.get( ' versionString' ) &&
index !== ( '/' + routeModel.get( 'versionString' ).slice( 0, -1 ) )
// Single items end with a regex, or a special case word.
if ( modelRegex.test( index ) ) {
modelRoutes.push( { index: index, route: route } );
// Collections end in a name.
collectionRoutes.push( { index: index, route: route } );
* Base the class name on the route endpoint.
_.each( modelRoutes, function( modelRoute ) {
// Extract the name and any parent from the route.
routeName = wp.api.utils.extractRoutePart( modelRoute.index, 2, routeModel.get( 'versionString' ), true ),
parentName = wp.api.utils.extractRoutePart( modelRoute.index, 1, routeModel.get( 'versionString' ), false ),
routeEnd = wp.api.utils.extractRoutePart( modelRoute.index, 1, routeModel.get( 'versionString' ), true );
// Clear the parent part of the rouite if its actually the version string.
if ( parentName === routeModel.get( 'versionString' ) ) {
// Handle the special case of the 'me' route.
if ( 'me' === routeEnd ) {
// If the model has a parent in its route, add that to its class name.
if ( '' !== parentName && parentName !== routeName ) {
modelClassName = wp.api.utils.capitalizeAndCamelCaseDashes( parentName ) + wp.api.utils.capitalizeAndCamelCaseDashes( routeName );
modelClassName = mapping.models[ modelClassName ] || modelClassName;
loadingObjects.models[ modelClassName ] = wp.api.WPApiBaseModel.extend( {
// Return a constructed url based on the parent and id.
routeModel.get( 'apiRoot' ) +
routeModel.get( 'versionString' ) +
( ( _.isUndefined( this.get( 'parent' ) ) || 0 === this.get( 'parent' ) ) ?
( _.isUndefined( this.get( 'parent_post' ) ) ? '' : this.get( 'parent_post' ) + '/' ) :
this.get( 'parent' ) + '/' ) +
if ( ! _.isUndefined( this.get( 'id' ) ) ) {
url += '/' + this.get( 'id' );
// Track nonces on the Endpoint 'routeModel'.
return routeModel.get( 'nonce' );
endpointModel: routeModel,
// Include a reference to the original route object.
// Include a reference to the original class name.
// Include the array of route methods for easy reference.
methods: modelRoute.route.methods,
// Include the array of route endpoints for easy reference.
endpoints: modelRoute.route.endpoints
// This is a model without a parent in its route.
modelClassName = wp.api.utils.capitalizeAndCamelCaseDashes( routeName );
modelClassName = mapping.models[ modelClassName ] || modelClassName;
loadingObjects.models[ modelClassName ] = wp.api.WPApiBaseModel.extend( {
// Function that returns a constructed url based on the ID.
var url = routeModel.get( 'apiRoot' ) +
routeModel.get( 'versionString' ) +
( ( 'me' === routeName ) ? 'users/me' : routeName );
if ( ! _.isUndefined( this.get( 'id' ) ) ) {
url += '/' + this.get( 'id' );
// Track nonces at the Endpoint level.
return routeModel.get( 'nonce' );
endpointModel: routeModel,
// Include a reference to the original route object.
// Include a reference to the original class name.
// Include the array of route methods for easy reference.
methods: modelRoute.route.methods,
// Include the array of route endpoints for easy reference.
endpoints: modelRoute.route.endpoints
// Add defaults to the new model, pulled form the endpoint.
wp.api.utils.decorateFromRoute(
modelRoute.route.endpoints,
loadingObjects.models[ modelClassName ],
routeModel.get( 'versionString' )
* Construct the collections.
* Base the class name on the route endpoint.
_.each( collectionRoutes, function( collectionRoute ) {
// Extract the name and any parent from the route.
var collectionClassName, modelClassName,
routeName = collectionRoute.index.slice( collectionRoute.index.lastIndexOf( '/' ) + 1 ),
parentName = wp.api.utils.extractRoutePart( collectionRoute.index, 1, routeModel.get( 'versionString' ), false );
// If the collection has a parent in its route, add that to its class name.
if ( '' !== parentName && parentName !== routeName && routeModel.get( 'versionString' ) !== parentName ) {
collectionClassName = wp.api.utils.capitalizeAndCamelCaseDashes( parentName ) + wp.api.utils.capitalizeAndCamelCaseDashes( routeName );
modelClassName = mapping.models[ collectionClassName ] || collectionClassName;
collectionClassName = mapping.collections[ collectionClassName ] || collectionClassName;
loadingObjects.collections[ collectionClassName ] = wp.api.WPApiBaseCollection.extend( {
// Function that returns a constructed url passed on the parent.
return routeModel.get( 'apiRoot' ) + routeModel.get( 'versionString' ) +
( ( _.isUndefined( this.parent ) || '' === this.parent ) ?
( _.isUndefined( this.get( 'parent_post' ) ) ? '' : this.get( 'parent_post' ) + '/' ) :
// Specify the model that this collection contains.
model: function( attrs, options ) {
return new loadingObjects.models[ modelClassName ]( attrs, options );
// Track nonces at the Endpoint level.
return routeModel.get( 'nonce' );
endpointModel: routeModel,
// Include a reference to the original class name.
name: collectionClassName,
// Include a reference to the original route object.
// Include the array of route methods for easy reference.
methods: collectionRoute.route.methods
// This is a collection without a parent in its route.
collectionClassName = wp.api.utils.capitalizeAndCamelCaseDashes( routeName );
modelClassName = mapping.models[ collectionClassName ] || collectionClassName;
collectionClassName = mapping.collections[ collectionClassName ] || collectionClassName;
loadingObjects.collections[ collectionClassName ] = wp.api.WPApiBaseCollection.extend( {
// For the url of a root level collection, use a string.
return routeModel.get( 'apiRoot' ) + routeModel.get( 'versionString' ) + routeName;
// Specify the model that this collection contains.
model: function( attrs, options ) {
return new loadingObjects.models[ modelClassName ]( attrs, options );
// Track nonces at the Endpoint level.
return routeModel.get( 'nonce' );
endpointModel: routeModel,
// Include a reference to the original class name.
name: collectionClassName,
// Include a reference to the original route object.
// Include the array of route methods for easy reference.
methods: collectionRoute.route.methods
// Add defaults to the new model, pulled form the endpoint.
wp.api.utils.decorateFromRoute( collectionRoute.route.endpoints, loadingObjects.collections[ collectionClassName ] );
// Add mixins and helpers for each of the models.
_.each( loadingObjects.models, function( model, index ) {
loadingObjects.models[ index ] = wp.api.utils.addMixinsAndHelpers( model, index, loadingObjects );
// Set the routeModel models and collections.
routeModel.set( 'models', loadingObjects.models );
routeModel.set( 'collections', loadingObjects.collections );
wp.api.endpoints = new Backbone.Collection();
* Initialize the wp-api, optionally passing the API root.