/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js
JavaScript | 303 lines | 220 code | 59 blank | 24 comment | 37 complexity | e1c65ba3e1b280b12090fa94870cb863 MD5 | raw file
- var toolbox = {};
- toolbox.getMainCollection = function (response) {
- return _.without(_.keys(response), 'links', 'linked', 'meta')[0];
- };
- // from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
- toolbox.generateUUID = function() {
- var d = new Date().getTime();
- var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = (d + Math.random()*16)%16 | 0;
- d = Math.floor(d/16);
- return (c=='x' ? r : (r&0x7|0x8)).toString(16);
- });
- return uuid;
- };
- toolbox.batchRequest = function(klass, ids, amount, success) {
- // batch requests to avoid GET length limits
- var grouped;
- var batch_size = 75;
- if (ids.length <= batch_size) {
- grouped = _.groupBy(ids, function(element, index){
- return Math.floor(index/batch_size);
- });
- } else {
- grouped = {1: ids};
- }
- return( _.map(grouped, function(id_group, index) {
- var linkedClass = klass;
- if (id_group.length > 0) {
- linkedClass = linkedClass.extend({
- url: function() { return(klass.prototype.url.call() + "/" + id_group.join(',')); }
- });
- }
- var linkedCollection = new linkedClass();
- return linkedCollection.fetch({'reset' : true, 'success' : success});
- }));
- };
- Backbone.JSONAPIModel = Backbone.Model.extend({
- // the following two methods, and batchRequest, should be folded into
- // Collection.fetch (and Model.fetch?)
- resolveLink: function(name, klass, superset) {
- if ( _.isUndefined(this.get('links')[name]) ) {
- this.linked[name] = new klass();
- } else {
- context = this;
- var records = superset.filter(function(obj) {
- return(context.get('links')[name].indexOf(obj.get('id')) > -1);
- });
- if ( _.isUndefined(this.linked[name]) ) {
- this.linked[name] = new klass();
- }
- this.linked[name].add(records);
- }
- },
- resolveLinks: function(name_klass_h) {
- var context = this;
- return _.flatten( _.map(name_klass_h, function(klass, name) {
- if ( _.isUndefined(context.linked) ) {
- context.linked = {};
- }
- var links_name = context.get('links')[name];
- if ( !_.isUndefined(links_name) && !_.isEmpty(links_name) ) {
- var success = function(resultCollection, response, options) {
- context.resolveLink(name, klass, resultCollection);
- };
- return toolbox.batchRequest(klass, links_name, 75, success);
- } else {
- return($.when(context.resolveLink(name, klass, new Array())));
- }
- }));
- },
- parse: function (response) {
- if (response === undefined) {
- return;
- }
- if (response._alreadyBBJSONAPIParsed) {
- delete response._alreadyBBJSONAPIParsed;
- return response;
- }
- var mainCollection = toolbox.getMainCollection(response);
- var obj = response[mainCollection][0];
- return obj;
- },
- // post: function(urlType, attrs, options) {
- // if ( _.isUndefined(options) || _.isNull(options) ) {
- // options = {};
- // }
- // postData = {}
- // postData[urlType] = [attrs];
- // return(this.save({}, _.extend(options, {
- // contentType: 'application/vnd.api+json',
- // data: JSON.stringify(postData)
- // })));
- // },
- // NB: should not be exported
- savePatch: function(attrs, patch, options) {
- if ( _.isUndefined(options) || _.isNull(options) ) {
- options = {};
- }
- return(this.save(attrs, _.extend(options, {
- data: JSON.stringify(patch),
- patch: true,
- contentType: 'application/json-patch+json'
- })));
- },
- patch: function(urlType, attrs, options) {
- if (attrs == null) {
- attrs = {};
- }
- var context = this;
- var patch = _.inject(attrs, function(memo, val, key) {
- // skip if not a simple attribute value
- if ( (key == 'links') || _.isObject(val) || _.isArray(val) ) {
- return memo;
- }
- memo.push({
- op: 'replace',
- path: '/' + urlType + '/0/' + key,
- value: val
- });
- return memo;
- }, new Array());
- this.savePatch(attrs, patch, options);
- },
- // singular operation only -- TODO batch up and submit en masse
- addLinked: function(urlType, type, obj, options) {
- var id = obj.get('id');
- var patch = [{
- op: 'add',
- path: '/' + urlType + '/0/links/' + type + '/-',
- value: id
- }];
- this.savePatch({}, patch, {}, options);
- if ( _.isUndefined(this.get('links')[type]) ) {
- this.get('links')[type] = new Array();
- }
- this.get('links')[type].push(id);
- this.linked[type].add(obj);
- },
- // singular operation only -- TODO batch up and submit en masse
- removeLinked: function(urlType, type, obj, options) {
- var id = obj.get('id');
- var patch = [{
- op: 'remove',
- path: '/' + urlType + '/0/links/' + type + '/' + id,
- }];
- this.savePatch({}, patch, {}, options);
- if ( _.isUndefined(this.get('links')[type]) ) {
- this.get('links')[type] = new Array();
- }
- this.get('links')[type] = _.without(this.get('links')[type], id);
- this.linked[type].remove(obj);
- },
- urlRoot: function() { return(flapjack.api_url + "/" + this.name); },
- // can only be called from inside a 'change' event
- setDirty: function() {
- if ( !this.hasChanged() ) {
- return;
- }
- this.dirty = true;
- if ( _.isUndefined(this.clean) ) {
- this.clean = {};
- }
- var context = this;
- // foreach changed attribute, merge previous value into a 'clean' hash
- // (only if the key isn't already in there)
- _.each(_.without(_.keys(this.changedAttributes()), _.keys(this.clean)), function(key) {
- context.clean[key] = context.previous(key);
- });
- },
- // can only be called from outside a change event
- revertClean: function() {
- if ( !_.isUndefined(this.clean) ) {
- this.set(this.clean, {silent: true});
- }
- this.clean = {};
- this.dirty = false;
- },
- sync: function(method, model, options) {
- if ( method == 'create' ) {
- // patch sets its own content type, get and delete don't have body content
- data = {};
- data[model.name] = [options.attrs || model.toJSON(options)];
- options['data'] = JSON.stringify(data)
- options['contentType'] = 'application/vnd.api+json';
- }
- var succ = function(data, response, opts) {
- model.clean = {};
- model.dirty = false;
- };
- if ( _.isUndefined(options['success']) ) {
- options['success'] = succ;
- } else {
- options['success'] = [options['success'], succ];
- }
- Backbone.sync(method, model, options);
- }
- });
- Backbone.JSONAPICollection = Backbone.Collection.extend({
- resolveLinks: function(name_klass_h) {
- if ( _.isUndefined(this.linked) ) {
- this.linked = {};
- }
- var context = this;
- _.each(name_klass_h, function(klass, name) {
- if ( !_.isUndefined(context.links[name]) && !_.isEmpty(context.links[name]) ) {
- context.linked[name] = new klass();
- var success = function(resultCollection, response, options) {
- context.forEach(function(obj, index) {
- if ( _.isUndefined(obj.linked) ) {
- obj.linked = {};
- }
- obj.resolveLink(name, klass, resultCollection);
- });
- }
- toolbox.batchRequest(klass, context.links[name], 75, success);
- }
- });
- },
- parse: function (response) {
- if (response === undefined) {
- return;
- }
- this.links = {};
- var context = this;
- var mainCollection = toolbox.getMainCollection(response);
- var objects = _.map(response[mainCollection], function (obj) {
- _.each(obj.links, function(ids, name) {
- if ( _.isUndefined(context.links[name]) ) {
- context.links[name] = new Array();
- }
- if ( _.isArray(ids) ) {
- context.links[name] = context.links[name].concat(ids);
- }
- });
- obj._alreadyBBJSONAPIParsed = true;
- return obj;
- });
- this.links = _.reduce(this.links, function(memo, ids, name) {
- memo[name] = _.uniq(ids);
- return(memo);
- }, {});
- return objects;
- }
- });