Skip to content

Instantly share code, notes, and snippets.

@thenbrent
Created May 30, 2014 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thenbrent/d678f56adbcd02651266 to your computer and use it in GitHub Desktop.
Save thenbrent/d678f56adbcd02651266 to your computer and use it in GitHub Desktop.
(function( window, undefined ) {
'use strict';
function WP_API() {
this.models = {};
this.collections = {};
this.views = {};
}
window.wp = window.wp || {};
wp.api = wp.api || new WP_API();
})( window );
(function( window, undefined ) {
'use strict';
// ECMAScript 5 shim, from MDN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
if ( ! Date.prototype.toISOString ) {
var pad = function( number ) {
var r = String( number );
if ( r.length === 1 ) {
r = '0' + r;
}
return r;
};
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 ) +
'Z';
};
}
function WP_API_Utils() {
var origParse = Date.parse,
numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
this.parseISO8601 = function( date ) {
var timestamp, struct, i, k,
minutesOffset = 0;
// 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 ( struct[8] !== 'Z' && struct[9] !== undefined ) {
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] );
}
else {
timestamp = origParse ? origParse( date ) : NaN;
}
return timestamp;
};
}
window.wp = window.wp || {};
wp.api = wp.api || {};
wp.api.utils = wp.api.utils || new WP_API_Utils();
})( window );
/* global WP_API_Settings:false */
// Suppress warning about parse function's unused "options" argument:
/* jshint unused:false */
(function( wp, WP_API_Settings, Backbone, window, undefined ) {
'use strict';
var parseable_dates = [ 'date', 'modified' ];
/**
* Backbone model for single users
*
* @type {*}
*/
wp.api.models.User = Backbone.Model.extend( {
idAttribute: 'ID',
urlRoot: WP_API_Settings.root + '/users',
defaults: {
ID: null,
username: '',
email: '',
password: '',
name: '',
first_name: '',
last_name: '',
nickname: '',
slug: '',
URL: '',
avatar: '',
meta: {
links: {}
}
},
avatar: function( size ) {
return this.get( 'avatar' ) + '&s=' + size;
}
});
/**
* Backbone model for a post status
*/
wp.api.models.PostStatus = Backbone.Model.extend( {
idAttribute: 'slug',
urlRoot: WP_API_Settings.root + '/posts/statuses',
defaults: {
slug: null,
name: '',
'public': true,
'protected': false,
'private': false,
queryable: true,
show_in_list: true,
meta: {
links: {}
}
},
/**
* This model is read only
*/
save: function() {
return false;
},
'delete': function() {
return false;
}
});
/**
* Model for taxonomy
*/
wp.api.models.Taxonomy = Backbone.Model.extend({
idAttribute: 'name',
defaults: {
name: null,
slug: '',
labels: [],
types: [ 'post' ],
show_cloud: false,
hierarchical: false,
meta: {
links: {}
}
},
url: function() {
var name = this.get( 'name' );
name = name || '';
return WP_API_Settings.root + '/posts/types/' + this.defaultPostType() + '/taxonomies/' + name;
},
/**
* Use the first post type as the default one
*
* @return string
*/
defaultPostType: function() {
var types = this.get( 'types');
if ( typeof types !== 'undefined' && types[0] ) {
return types[0];
}
return null;
}
});
/**
* Backbone model for term
*/
wp.api.models.Term = Backbone.Model.extend({
idAttribute: 'ID',
type: 'post',
taxonomy: 'category',
initialize: function( attributes, options ) {
if ( typeof options !== 'undefined' ) {
if ( options.type ) {
this.type = options.type;
}
if ( options.taxonomy ) {
this.taxonomy = options.taxonomy;
}
}
},
url: function() {
var id = this.get( 'ID' );
id = id || '';
return WP_API_Settings.root + '/posts/types/' + this.type + '/taxonomies/' + this.taxonomy + '/terms/' + id;
},
parent: function() {
var term,
parent = this.get( 'parent' );
// Return null if we don't have a parent
if ( parent === 0 ) {
return null;
}
// Can we get this from its collection?
if ( this.collection ) {
return this.collection.get( parent );
} else {
// Otherwise, get the post directly
term = new wp.api.models.Term( {
ID: parent
});
// Note that this acts asynchronously
term.fetch();
return term;
}
},
defaults: {
ID: null,
name: '',
slug: '',
description: '',
parent: null,
count: 0,
link: '',
meta: {
links: {}
}
}
});
/**
* Backbone model for single posts
*
* @type {*}
*/
wp.api.models.Post = Backbone.Model.extend( {
idAttribute: 'ID',
urlRoot: WP_API_Settings.root + '/posts',
defaults: function() {
return {
ID: null,
title: '',
status: 'draft',
type: 'post',
author: new wp.api.models.User(),
content: '',
link: '',
'parent': 0,
date: new Date(),
date_gmt: new Date(),
modified: new Date(),
modified_gmt: new Date(),
format: 'standard',
slug: '',
guid: '',
excerpt: '',
menu_order: 0,
comment_status: 'open',
ping_status: 'open',
sticky: false,
date_tz: 'Etc/UTC',
modified_tz: 'Etc/UTC',
terms: {},
post_meta: {},
meta: {
links: {}
}
};
},
/**
* Serialize the entity
*
* Overriden for correct date handling
* @return {!Object} Serializable attributes
*/
toJSON: function() {
var attributes = _.clone( this.attributes );
// Serialize Date objects back into 8601 strings
_.each( parseable_dates, function ( key ) {
attributes[key] = attributes[key].toISOString();
});
return attributes;
},
/**
* Unserialize the entity
*
* Overriden for correct date handling
* @param {!Object} response Attributes parsed from JSON
* @param {!Object} options Request options
* @return {!Object} Fully parsed attributes
*/
parse: function( response ) {
// Parse dates into native Date objects
_.each( parseable_dates, function ( key ) {
if ( ! ( key in response ) ) {
return;
}
var timestamp = wp.api.utils.parseISO8601( response[key] );
response[key] = new Date( timestamp );
});
// Parse the author into a User object
response.author = new wp.api.models.User( { username: response.author } );
return response;
},
/**
* Get parent post
*
* @return {wp.api.models.Post} Parent post, null if not found
*/
parent: function() {
var post,
parent = this.get( 'parent' );
// Return null if we don't have a parent
if ( parent === 0 ) {
return null;
}
// Can we get this from its collection?
if ( this.collection ) {
return this.collection.get( parent );
} else {
// Otherwise, get the post directly
post = new wp.api.models.Post( {
ID: parent
});
// Note that this acts asynchronously
post.fetch();
return post;
}
}
});
/**
* Backbone model for pages
*/
wp.api.models.Page = wp.api.models.Post.extend( {
idAttribute: 'ID',
urlRoot: WP_API_Settings.root + '/pages',
defaults: {
ID: null,
title: '',
status: 'publish',
type: 'page',
author: new wp.api.models.User(),
content: '',
parent: 0,
link: '',
date: new Date(),
modified: new Date(),
date_gmt: new Date(),
modified_gmt: new Date(),
date_tz: 'Etc/UTC',
modified_tz: 'Etc/UTC',
format: 'standard',
slug: '',
guid: '',
excerpt: '',
menu_order: 0,
comment_status: 'closed',
ping_status: 'open',
sticky: false,
password: '',
meta: {
links: {}
},
featured_image: null,
terms: []
}
});
/**
* Backbone model for pages
*/
wp.api.models.Revision = wp.api.models.Post.extend( {
url: function() {
var parent_id = this.get( 'parent' );
parent_id = parent_id || '';
return WP_API_Settings.root + '/posts/' + parent_id + '/revisions/';
}
});
/**
* Backbone model for media items
*/
wp.api.models.Media = wp.api.models.Post.extend( {
idAttribute: 'ID',
urlRoot: WP_API_Settings.root + '/media',
defaults: {
ID: null,
title: '',
status: 'inherit',
type: 'attachment',
author: {},
content: '',
parent: 0,
link: '',
date: new Date(),
modified: new Date(),
date_gmt: new Date(),
modified_gmt: new Date(),
format: 'standard',
slug: '',
guid: '',
excerpt: null,
menu_order: 0,
comment_status: 'open',
ping_status: 'open',
sticky: false,
date_tz: 'Etc/UTC',
modified_tz: 'Etc/UTC',
meta: {
links: {}
},
terms: [],
source: '',
is_image: true,
attachment_meta: {}
}
});
/**
* Backbone model for comments
*/
wp.api.models.Comment = Backbone.Model.extend( {
idAttribute: 'ID',
defaults: {
ID: null,
post: null,
content: '',
status: 'approved',
type: 'comment',
parent: 0,
author: new wp.api.models.User(),
date: new Date(),
date_gmt: new Date(),
date_tz: 'Etc/UTC',
meta: {
links: {}
}
},
url: function() {
var post_id = this.get( 'post' );
post_id = post_id || '';
var id = this.get( 'ID' );
id = id || '';
return WP_API_Settings.root + '/posts/' + post_id + '/comments/' + id;
},
parse: function( response ) {
_.each( parseable_dates, function ( key ) {
if ( ! ( key in response ) ) {
return;
}
var timestamp = wp.api.utils.parseISO8601( response[key] );
response[key] = new Date( timestamp );
});
// Parse the author into a User object
response.author = new wp.api.models.User( { username: response.author } );
return response;
},
toJSON: function() {
var attributes = _.clone( this.attributes );
// Serialize Date objects back into 8601 strings
_.each( parseable_dates, function ( key ) {
attributes[key] = attributes[key].toISOString();
});
return attributes;
},
parent: function() {
var comment,
parent = this.get( 'parent' );
// Return null if we don't have a parent
if ( parent === 0 ) {
return null;
}
// Can we get this from its collection?
if ( this.collection ) {
return this.collection.get(parent);
} else {
// Otherwise, get the post directly
comment = new wp.api.models.Comment({
ID: parent
});
// Note that this acts asynchronously
comment.fetch();
return comment;
}
}
});
/**
* Backbone model for single post types
*/
wp.api.models.PostType = Backbone.Model.extend( {
idAttribute: 'slug',
urlRoot: WP_API_Settings.root + '/posts/types',
defaults: {
slug: null,
name: '',
description: '',
labels: {},
queryable: false,
searchable: false,
hierarchical: false,
meta: {
links: {}
},
taxonomies: []
},
/**
* This is a read only model
*
* @returns {boolean}
*/
save: function () {
return false;
},
'delete': function () {
return false;
}
});
})( wp, WP_API_Settings, Backbone, window );
/* global WP_API_Settings:false */
(function( wp, WP_API_Settings, Backbone, _, window, undefined ) {
'use strict';
/**
* wp.api.collections.Posts
*/
wp.api.collections.Posts = Backbone.Collection.extend({
url: WP_API_Settings.root + '/posts',
model: wp.api.models.Post
});
/**
* Backbone collection for pages
*/
wp.api.collections.Pages = Backbone.Collection.extend({
url: WP_API_Settings.root + '/pages',
model: wp.api.models.Page
});
/**
* Backbone users collection
*/
wp.api.collections.Users = Backbone.Collection.extend({
url: WP_API_Settings.root + '/users',
model: wp.api.models.User
});
/**
* Backbone post statuses collection
*/
wp.api.collections.PostStatuses = Backbone.Collection.extend({
url: WP_API_Settings.root + '/posts/statuses',
model: wp.api.models.PostStatus
});
/**
* Backbone media library collection
*/
wp.api.collections.MediaLibrary = Backbone.Collection.extend({
url: WP_API_Settings.root + '/media',
model: wp.api.models.Media
});
/**
* Backbone taxonomy collection
*/
wp.api.collections.Taxonomies = Backbone.Collection.extend({
model: wp.api.models.Taxonomy,
type: 'post',
initialize: function( models, options ) {
if ( options && options.type ) {
this.type = options.type;
}
},
url: function() {
return WP_API_Settings.root + '/posts/types/' + this.type + '/taxonomies/';
}
});
/**
* Backbone comment collection
*/
wp.api.collections.Comments = Backbone.Collection.extend({
model: wp.api.models.Comment,
post: null,
initialize: function( models, options ) {
if ( options && options.post ) {
this.post = options.post;
}
},
url: function() {
return WP_API_Settings.root + '/posts/' + this.post + '/comments';
}
});
/**
* Backbone post type collection
*/
wp.api.collections.PostTypes = Backbone.Collection.extend({
model: wp.api.models.PostType,
url: WP_API_Settings.root + '/posts/types'
});
/**
* Backbone terms collection
*/
wp.api.collections.Terms = Backbone.Collection.extend({
model: wp.api.models.Term,
type: 'post',
taxonomy: 'category',
initialize: function( models, options ) {
if ( typeof options !== 'undefined' ) {
if ( options.type ) {
this.type = options.type;
}
if ( options.taxonomy ) {
this.taxonomy = options.taxonomy;
}
}
this.on( 'add', _.bind( this.addModel, this ) );
},
addModel: function( model ) {
model.type = this.type;
model.taxonomy = this.taxonomy;
},
url: function() {
return WP_API_Settings.root + '/posts/types/' + this.type + '/taxonomies/' + this.taxonomy + '/terms/';
}
});
/**
* Backbone revisions collection
*/
wp.api.collections.Revisions = Backbone.Collection.extend( {
model: wp.api.models.Revision,
post: null,
initialize: function( models, options ) {
if ( options && options.post ) {
this.post = options.post;
}
},
url: function() {
return WP_API_Settings.root + '/posts/' + this.post + '/revisions';
}
});
})( wp, WP_API_Settings, Backbone, _, window );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment