123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356 |
- /**
- * Restful Resources service for AngularJS apps
- * @version v1.4.0 - 2015-04-03 * @link https://github.com/mgonto/restangular
- * @author Martin Gontovnikas <martin@gon.to>
- * @license MIT License, http://www.opensource.org/licenses/MIT
- */(function() {
- var restangular = angular.module('restangular', []);
- restangular.provider('Restangular', function() {
- // Configuration
- var Configurer = {};
- Configurer.init = function(object, config) {
- object.configuration = config;
- /**
- * Those are HTTP safe methods for which there is no need to pass any data with the request.
- */
- var safeMethods= ['get', 'head', 'options', 'trace', 'getlist'];
- config.isSafe = function(operation) {
- return _.contains(safeMethods, operation.toLowerCase());
- };
- var absolutePattern = /^https?:\/\//i;
- config.isAbsoluteUrl = function(string) {
- return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
- string && absolutePattern.test(string) :
- config.absoluteUrl;
- };
- config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
- object.setSelfLinkAbsoluteUrl = function(value) {
- config.absoluteUrl = value;
- };
- /**
- * This is the BaseURL to be used with Restangular
- */
- config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
- object.setBaseUrl = function(newBaseUrl) {
- config.baseUrl = /\/$/.test(newBaseUrl) ?
- newBaseUrl.substring(0, newBaseUrl.length-1) :
- newBaseUrl;
- return this;
- };
- /**
- * Sets the extra fields to keep from the parents
- */
- config.extraFields = config.extraFields || [];
- object.setExtraFields = function(newExtraFields) {
- config.extraFields = newExtraFields;
- return this;
- };
- /**
- * Some default $http parameter to be used in EVERY call
- **/
- config.defaultHttpFields = config.defaultHttpFields || {};
- object.setDefaultHttpFields = function(values) {
- config.defaultHttpFields = values;
- return this;
- };
- config.withHttpValues = function(httpLocalConfig, obj) {
- return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
- };
- config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
- object.setEncodeIds = function(encode) {
- config.encodeIds = encode;
- };
- config.defaultRequestParams = config.defaultRequestParams || {
- get: {},
- post: {},
- put: {},
- remove: {},
- common: {}
- };
- object.setDefaultRequestParams = function(param1, param2) {
- var methods = [],
- params = param2 || param1;
- if (!_.isUndefined(param2)) {
- if (_.isArray(param1)) {
- methods = param1;
- } else {
- methods.push(param1);
- }
- } else {
- methods.push('common');
- }
- _.each(methods, function (method) {
- config.defaultRequestParams[method] = params;
- });
- return this;
- };
- object.requestParams = config.defaultRequestParams;
- config.defaultHeaders = config.defaultHeaders || {};
- object.setDefaultHeaders = function(headers) {
- config.defaultHeaders = headers;
- object.defaultHeaders = config.defaultHeaders;
- return this;
- };
- object.defaultHeaders = config.defaultHeaders;
- /**
- * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
- **/
- config.methodOverriders = config.methodOverriders || [];
- object.setMethodOverriders = function(values) {
- var overriders = _.extend([], values);
- if (config.isOverridenMethod('delete', overriders)) {
- overriders.push('remove');
- }
- config.methodOverriders = overriders;
- return this;
- };
- config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
- object.setJsonp = function(active) {
- config.jsonp = active;
- };
- config.isOverridenMethod = function(method, values) {
- var search = values || config.methodOverriders;
- return !_.isUndefined(_.find(search, function(one) {
- return one.toLowerCase() === method.toLowerCase();
- }));
- };
- /**
- * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
- **/
- config.urlCreator = config.urlCreator || 'path';
- object.setUrlCreator = function(name) {
- if (!_.has(config.urlCreatorFactory, name)) {
- throw new Error('URL Path selected isn\'t valid');
- }
- config.urlCreator = name;
- return this;
- };
- /**
- * You can set the restangular fields here. The 3 required fields for Restangular are:
- *
- * id: Id of the element
- * route: name of the route of this element
- * parentResource: the reference to the parent resource
- *
- * All of this fields except for id, are handled (and created) by Restangular. By default,
- * the field values will be id, route and parentResource respectively
- */
- config.restangularFields = config.restangularFields || {
- id: 'id',
- route: 'route',
- parentResource: 'parentResource',
- restangularCollection: 'restangularCollection',
- cannonicalId: '__cannonicalId',
- etag: 'restangularEtag',
- selfLink: 'href',
- get: 'get',
- getList: 'getList',
- put: 'put',
- post: 'post',
- remove: 'remove',
- head: 'head',
- trace: 'trace',
- options: 'options',
- patch: 'patch',
- getRestangularUrl: 'getRestangularUrl',
- getRequestedUrl: 'getRequestedUrl',
- putElement: 'putElement',
- addRestangularMethod: 'addRestangularMethod',
- getParentList: 'getParentList',
- clone: 'clone',
- ids: 'ids',
- httpConfig: '_$httpConfig',
- reqParams: 'reqParams',
- one: 'one',
- all: 'all',
- several: 'several',
- oneUrl: 'oneUrl',
- allUrl: 'allUrl',
- customPUT: 'customPUT',
- customPOST: 'customPOST',
- customDELETE: 'customDELETE',
- customGET: 'customGET',
- customGETLIST: 'customGETLIST',
- customOperation: 'customOperation',
- doPUT: 'doPUT',
- doPOST: 'doPOST',
- doDELETE: 'doDELETE',
- doGET: 'doGET',
- doGETLIST: 'doGETLIST',
- fromServer: 'fromServer',
- withConfig: 'withConfig',
- withHttpConfig: 'withHttpConfig',
- singleOne: 'singleOne',
- plain: 'plain',
- save: 'save',
- restangularized: 'restangularized'
- };
- object.setRestangularFields = function(resFields) {
- config.restangularFields =
- _.extend(config.restangularFields, resFields);
- return this;
- };
- config.isRestangularized = function(obj) {
- return !!obj[config.restangularFields.restangularized];
- };
- config.setFieldToElem = function(field, elem, value) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(_.initial(properties), function(prop) {
- idValue[prop] = {};
- idValue = idValue[prop];
- });
- idValue[_.last(properties)] = value;
- return this;
- };
- config.getFieldFromElem = function(field, elem) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(properties, function(prop) {
- if (idValue) {
- idValue = idValue[prop];
- }
- });
- return angular.copy(idValue);
- };
- config.setIdToElem = function(elem, id /*, route */) {
- config.setFieldToElem(config.restangularFields.id, elem, id);
- return this;
- };
- config.getIdFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.id, elem);
- };
- config.isValidId = function(elemId) {
- return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
- };
- config.setUrlToElem = function(elem, url /*, route */) {
- config.setFieldToElem(config.restangularFields.selfLink, elem, url);
- return this;
- };
- config.getUrlFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.selfLink, elem);
- };
- config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
- object.setUseCannonicalId = function(value) {
- config.useCannonicalId = value;
- return this;
- };
- config.getCannonicalIdFromElem = function(elem) {
- var cannonicalId = elem[config.restangularFields.cannonicalId];
- var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
- return actualId;
- };
- /**
- * Sets the Response parser. This is used in case your response isn't directly the data.
- * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
- * you can extract this data which is the one that needs wrapping
- *
- * The ResponseExtractor is a function that receives the response and the method executed.
- */
- config.responseInterceptors = config.responseInterceptors || [];
- config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */) {
- return data;
- };
- config.responseExtractor = function(data, operation, what, url, response, deferred) {
- var interceptors = angular.copy(config.responseInterceptors);
- interceptors.push(config.defaultResponseInterceptor);
- var theData = data;
- _.each(interceptors, function(interceptor) {
- theData = interceptor(theData, operation,
- what, url, response, deferred);
- });
- return theData;
- };
- object.addResponseInterceptor = function(extractor) {
- config.responseInterceptors.push(extractor);
- return this;
- };
- config.errorInterceptors = config.errorInterceptors || [];
- object.addErrorInterceptor = function(interceptor) {
- config.errorInterceptors.push(interceptor);
- return this;
- };
- object.setResponseInterceptor = object.addResponseInterceptor;
- object.setResponseExtractor = object.addResponseInterceptor;
- object.setErrorInterceptor = object.addErrorInterceptor;
- /**
- * Response interceptor is called just before resolving promises.
- */
- /**
- * Request interceptor is called before sending an object to the server.
- */
- config.requestInterceptors = config.requestInterceptors || [];
- config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- return {
- element: element,
- headers: headers,
- params: params,
- httpConfig: httpConfig
- };
- };
- config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- var interceptors = angular.copy(config.requestInterceptors);
- var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
- return _.reduce(interceptors, function(request, interceptor) {
- return _.extend(request, interceptor(request.element, operation,
- path, url, request.headers, request.params, request.httpConfig));
- }, defaultRequest);
- };
- object.addRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
- return {
- headers: headers,
- params: params,
- element: interceptor(elem, operation, path, url),
- httpConfig: httpConfig
- };
- });
- return this;
- };
- object.setRequestInterceptor = object.addRequestInterceptor;
- object.addFullRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(interceptor);
- return this;
- };
- object.setFullRequestInterceptor = object.addFullRequestInterceptor;
- config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnBeforeElemRestangularized = function(post) {
- config.onBeforeElemRestangularized = post;
- return this;
- };
- object.setRestangularizePromiseInterceptor = function(interceptor) {
- config.restangularizePromiseInterceptor = interceptor;
- return this;
- };
- /**
- * This method is called after an element has been "Restangularized".
- *
- * It receives the element, a boolean indicating if it's an element or a collection
- * and the name of the model
- *
- */
- config.onElemRestangularized = config.onElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnElemRestangularized = function(post) {
- config.onElemRestangularized = post;
- return this;
- };
- config.shouldSaveParent = config.shouldSaveParent || function() {
- return true;
- };
- object.setParentless = function(values) {
- if (_.isArray(values)) {
- config.shouldSaveParent = function(route) {
- return !_.contains(values, route);
- };
- } else if (_.isBoolean(values)) {
- config.shouldSaveParent = function() {
- return !values;
- };
- }
- return this;
- };
- /**
- * This lets you set a suffix to every request.
- *
- * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
- * in here.
- *
- *
- * By default, the suffix is null
- */
- config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
- object.setRequestSuffix = function(newSuffix) {
- config.suffix = newSuffix;
- return this;
- };
- /**
- * Add element transformers for certain routes.
- */
- config.transformers = config.transformers || {};
- object.addElementTransformer = function(type, secondArg, thirdArg) {
- var isCollection = null;
- var transformer = null;
- if (arguments.length === 2) {
- transformer = secondArg;
- } else {
- transformer = thirdArg;
- isCollection = secondArg;
- }
- var typeTransformers = config.transformers[type];
- if (!typeTransformers) {
- typeTransformers = config.transformers[type] = [];
- }
- typeTransformers.push(function(coll, elem) {
- if (_.isNull(isCollection) || (coll === isCollection)) {
- return transformer(elem);
- }
- return elem;
- });
- return object;
- };
- object.extendCollection = function(route, fn) {
- return object.addElementTransformer(route, true, fn);
- };
- object.extendModel = function(route, fn) {
- return object.addElementTransformer(route, false, fn);
- };
- config.transformElem = function(elem, isCollection, route, Restangular, force) {
- if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
- return elem;
- }
- var typeTransformers = config.transformers[route];
- var changedElem = elem;
- if (typeTransformers) {
- _.each(typeTransformers, function(transformer) {
- changedElem = transformer(isCollection, changedElem);
- });
- }
- return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
- };
- config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
- false :
- config.transformLocalElements;
- object.setTransformOnlyServerElements = function(active) {
- config.transformLocalElements = !active;
- };
- config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
- object.setFullResponse = function(full) {
- config.fullResponse = full;
- return this;
- };
- //Internal values and functions
- config.urlCreatorFactory = {};
- /**
- * Base URL Creator. Base prototype for everything related to it
- **/
- var BaseCreator = function() {
- };
- BaseCreator.prototype.setConfig = function(config) {
- this.config = config;
- return this;
- };
- BaseCreator.prototype.parentsArray = function(current) {
- var parents = [];
- while(current) {
- parents.push(current);
- current = current[this.config.restangularFields.parentResource];
- }
- return parents.reverse();
- };
- function RestangularResource(config, $http, url, configurer) {
- var resource = {};
- _.each(_.keys(configurer), function(key) {
- var value = configurer[key];
- // Add default parameters
- value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
- // We don't want the ? if no params are there
- if (_.isEmpty(value.params)) {
- delete value.params;
- }
- if (config.isSafe(value.method)) {
- resource[key] = function() {
- return $http(_.extend(value, {
- url: url
- }));
- };
- } else {
- resource[key] = function(data) {
- return $http(_.extend(value, {
- url: url,
- data: data
- }));
- };
- }
- });
- return resource;
- }
- BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag,operation) {
- var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
- var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
- if (etag) {
- if (!config.isSafe(operation)) {
- headers['If-Match'] = etag;
- } else {
- headers['If-None-Match'] = etag;
- }
- }
- var url = this.base(current);
- if (what) {
- var add = '';
- if (!/\/$/.test(url)) {
- add += '/';
- }
- add += what;
- url += add;
- }
- if (this.config.suffix &&
- url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
- !this.config.getUrlFromElem(current)) {
- url += this.config.suffix;
- }
- current[this.config.restangularFields.httpConfig] = undefined;
- return RestangularResource(this.config, $http, url, {
- getList: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
- get: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
- jsonp: this.config.withHttpValues(localHttpConfig,
- {method: 'jsonp',
- params: params,
- headers: headers}),
- put: this.config.withHttpValues(localHttpConfig,
- {method: 'PUT',
- params: params,
- headers: headers}),
- post: this.config.withHttpValues(localHttpConfig,
- {method: 'POST',
- params: params,
- headers: headers}),
- remove: this.config.withHttpValues(localHttpConfig,
- {method: 'DELETE',
- params: params,
- headers: headers}),
- head: this.config.withHttpValues(localHttpConfig,
- {method: 'HEAD',
- params: params,
- headers: headers}),
- trace: this.config.withHttpValues(localHttpConfig,
- {method: 'TRACE',
- params: params,
- headers: headers}),
- options: this.config.withHttpValues(localHttpConfig,
- {method: 'OPTIONS',
- params: params,
- headers: headers}),
- patch: this.config.withHttpValues(localHttpConfig,
- {method: 'PATCH',
- params: params,
- headers: headers})
- });
- };
- /**
- * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
- * This means that if you have an Account that then has a set of Buildings, a URL to a building
- * would be /accounts/123/buildings/456
- **/
- var Path = function() {
- };
- Path.prototype = new BaseCreator();
- Path.prototype.normalizeUrl = function (url){
- var parts = /(http[s]?:\/\/)?(.*)?/.exec(url);
- parts[2] = parts[2].replace(/[\\\/]+/g, '/');
- return (typeof parts[1] !== 'undefined')? parts[1] + parts[2] : parts[2];
- };
- Path.prototype.base = function(current) {
- var __this = this;
- return _.reduce(this.parentsArray(current), function(acum, elem) {
- var elemUrl;
- var elemSelfLink = __this.config.getUrlFromElem(elem);
- if (elemSelfLink) {
- if (__this.config.isAbsoluteUrl(elemSelfLink)) {
- return elemSelfLink;
- } else {
- elemUrl = elemSelfLink;
- }
- } else {
- elemUrl = elem[__this.config.restangularFields.route];
- if (elem[__this.config.restangularFields.restangularCollection]) {
- var ids = elem[__this.config.restangularFields.ids];
- if (ids) {
- elemUrl += '/' + ids.join(',');
- }
- } else {
- var elemId;
- if (__this.config.useCannonicalId) {
- elemId = __this.config.getCannonicalIdFromElem(elem);
- } else {
- elemId = __this.config.getIdFromElem(elem);
- }
- if (config.isValidId(elemId) && !elem.singleOne) {
- elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
- }
- }
- }
- acum = acum.replace(/\/$/, '') + '/' + elemUrl;
- return __this.normalizeUrl(acum);
- }, this.config.baseUrl);
- };
- Path.prototype.fetchUrl = function(current, what) {
- var baseUrl = this.base(current);
- if (what) {
- baseUrl += '/' + what;
- }
- return baseUrl;
- };
- Path.prototype.fetchRequestedUrl = function(current, what) {
- var url = this.fetchUrl(current, what);
- var params = current[config.restangularFields.reqParams];
- // From here on and until the end of fetchRequestedUrl,
- // the code has been kindly borrowed from angular.js
- // The reason for such code bloating is coherence:
- // If the user were to use this for cache management, the
- // serialization of parameters would need to be identical
- // to the one done by angular for cache keys to match.
- function sortedKeys(obj) {
- var keys = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- keys.push(key);
- }
- }
- return keys.sort();
- }
- function forEachSorted(obj, iterator, context) {
- var keys = sortedKeys(obj);
- for ( var i = 0; i < keys.length; i++) {
- iterator.call(context, obj[keys[i]], keys[i]);
- }
- return keys;
- }
- function encodeUriQuery(val, pctEncodeSpaces) {
- return encodeURIComponent(val).
- replace(/%40/gi, '@').
- replace(/%3A/gi, ':').
- replace(/%24/g, '$').
- replace(/%2C/gi, ',').
- replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
- }
- if (!params) { return url + (this.config.suffix || ''); }
- var parts = [];
- forEachSorted(params, function(value, key) {
- if (value === null || value === undefined) { return; }
- if (!angular.isArray(value)) { value = [value]; }
- angular.forEach(value, function(v) {
- if (angular.isObject(v)) {
- v = angular.toJson(v);
- }
- parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
- });
- });
- return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
- };
- config.urlCreatorFactory.path = Path;
- };
- var globalConfiguration = {};
- Configurer.init(this, globalConfiguration);
- this.$get = ['$http', '$q', function($http, $q) {
- function createServiceForConfiguration(config) {
- var service = {};
- var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
- urlHandler.setConfig(config);
- function restangularizeBase(parent, elem, route, reqParams, fromServer) {
- elem[config.restangularFields.route] = route;
- elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
- elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
- elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
- elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
- elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
- elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
- elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
- // Tag element as restangularized
- elem[config.restangularFields.restangularized] = true;
- // RequestLess connection
- elem[config.restangularFields.one] = _.bind(one, elem, elem);
- elem[config.restangularFields.all] = _.bind(all, elem, elem);
- elem[config.restangularFields.several] = _.bind(several, elem, elem);
- elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
- elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
- elem[config.restangularFields.fromServer] = !!fromServer;
- if (parent && config.shouldSaveParent(route)) {
- var parentId = config.getIdFromElem(parent);
- var parentUrl = config.getUrlFromElem(parent);
- var restangularFieldsForParent = _.union(
- _.values( _.pick(config.restangularFields, ['route', 'singleOne', 'parentResource']) ),
- config.extraFields
- );
- var parentResource = _.pick(parent, restangularFieldsForParent);
- if (config.isValidId(parentId)) {
- config.setIdToElem(parentResource, parentId, route);
- }
- if (config.isValidId(parentUrl)) {
- config.setUrlToElem(parentResource, parentUrl, route);
- }
- elem[config.restangularFields.parentResource] = parentResource;
- } else {
- elem[config.restangularFields.parentResource] = null;
- }
- return elem;
- }
- function one(parent, route, id, singleOne) {
- var error;
- if (_.isNumber(route) || _.isNumber(parent)) {
- error = 'You\'re creating a Restangular entity with the number ';
- error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
- throw new Error(error);
- }
- if (_.isUndefined(route)) {
- error = 'You\'re creating a Restangular entity either without the path. ';
- error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
- throw new Error(error);
- }
- var elem = {};
- config.setIdToElem(elem, id, route);
- config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
- return restangularizeElem(parent, elem , route, false);
- }
- function all(parent, route) {
- return restangularizeCollection(parent, [] , route, false);
- }
- function several(parent, route /*, ids */) {
- var collection = [];
- collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
- return restangularizeCollection(parent, collection , route, false);
- }
- function oneUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
- }
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeElem(parent, elem , route, false);
- }
- function allUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
- }
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeCollection(parent, elem , route, false);
- }
- // Promises
- function restangularizePromise(promise, isCollection, valueToFill) {
- promise.call = _.bind(promiseCall, promise);
- promise.get = _.bind(promiseGet, promise);
- promise[config.restangularFields.restangularCollection] = isCollection;
- if (isCollection) {
- promise.push = _.bind(promiseCall, promise, 'push');
- }
- promise.$object = valueToFill;
- if (config.restangularizePromiseInterceptor) {
- config.restangularizePromiseInterceptor(promise);
- }
- return promise;
- }
- function promiseCall(method) {
- var deferred = $q.defer();
- var callArgs = arguments;
- var filledValue = {};
- this.then(function(val) {
- var params = Array.prototype.slice.call(callArgs, 1);
- var func = val[method];
- func.apply(val, params);
- filledValue = val;
- deferred.resolve(val);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
- function promiseGet(what) {
- var deferred = $q.defer();
- var filledValue = {};
- this.then(function(val) {
- filledValue = val[what];
- deferred.resolve(filledValue);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
- function resolvePromise(deferred, response, data, filledValue) {
- _.extend(filledValue, data);
- // Trigger the full response interceptor.
- if (config.fullResponse) {
- return deferred.resolve(_.extend(response, {
- data: data
- }));
- } else {
- deferred.resolve(data);
- }
- }
- // Elements
- function stripRestangular(elem) {
- if (_.isArray(elem)) {
- var array = [];
- _.each(elem, function(value) {
- array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
- });
- return array;
- } else {
- return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
- }
- }
- function addCustomOperation(elem) {
- elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
- _.each(['put', 'post', 'get', 'delete'], function(oper) {
- _.each(['do', 'custom'], function(alias) {
- var callOperation = oper === 'delete' ? 'remove' : oper;
- var name = alias + oper.toUpperCase();
- var callFunction;
- if (callOperation !== 'put' && callOperation !== 'post') {
- callFunction = customFunction;
- } else {
- callFunction = function(operation, elem, path, params, headers) {
- return _.bind(customFunction, this)(operation, path, params, headers, elem);
- };
- }
- elem[name] = _.bind(callFunction, elem, callOperation);
- });
- });
- elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
- elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
- }
- function copyRestangularizedElement(fromElement, toElement) {
- var copiedElement = angular.copy(fromElement, toElement);
- return restangularizeElem(copiedElement[config.restangularFields.parentResource],
- copiedElement, copiedElement[config.restangularFields.route], true);
- }
- function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, false, route);
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- if (config.useCannonicalId) {
- localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
- }
- if (collection) {
- localElem[config.restangularFields.getParentList] = function() {
- return collection;
- };
- }
- localElem[config.restangularFields.restangularCollection] = false;
- localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
- localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.save] = _.bind(save, localElem);
- addCustomOperation(localElem);
- return config.transformElem(localElem, false, route, service, true);
- }
- function restangularizeCollection(parent, element, route, fromServer, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, true, route);
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- localElem[config.restangularFields.restangularCollection] = true;
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.get] = _.bind(getById, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
- addCustomOperation(localElem);
- return config.transformElem(localElem, true, route, service, true);
- }
- function restangularizeCollectionAndElements(parent, element, route) {
- var collection = restangularizeCollection(parent, element, route, false);
- _.each(collection, function(elem) {
- restangularizeElem(parent, elem, route, false);
- });
- return collection;
- }
- function getById(id, reqParams, headers){
- return this.customGET(id.toString(), reqParams, headers);
- }
- function putElementFunction(idx, params, headers) {
- var __this = this;
- var elemToPut = this[idx];
- var deferred = $q.defer();
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
- elemToPut.put(params, headers).then(function(serverElem) {
- var newArray = copyRestangularizedElement(__this);
- newArray[idx] = serverElem;
- filledArray = newArray;
- deferred.resolve(newArray);
- }, function(response) {
- deferred.reject(response);
- });
- return restangularizePromise(deferred.promise, true, filledArray);
- }
- function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
- var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
- var etag = response.headers('ETag');
- if (data && etag) {
- data[config.restangularFields.etag] = etag;
- }
- return data;
- }
- function fetchFunction(what, reqParams, headers) {
- var __this = this;
- var deferred = $q.defer();
- var operation = 'getList';
- var url = urlHandler.fetchUrl(this, what);
- var whatFetched = what || __this[config.restangularFields.route];
- var request = config.fullRequestInterceptor(null, operation,
- whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, whatFetched, service);
- var method = 'getList';
- if (config.jsonp) {
- method = 'jsonp';
- }
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
- // support empty response for getList() calls (some APIs respond with 204 and empty body)
- if (_.isUndefined(data) || '' === data) {
- data = [];
- }
- if (!_.isArray(data)) {
- throw new Error('Response for getList SHOULD be an array and not an object or something else');
- }
- var processedData = _.map(data, function(elem) {
- if (!__this[config.restangularFields.restangularCollection]) {
- return restangularizeElem(__this, elem, what, true, data);
- } else {
- return restangularizeElem(__this[config.restangularFields.parentResource],
- elem, __this[config.restangularFields.route], true, data);
- }
- });
- processedData = _.extend(data, processedData);
- if (!__this[config.restangularFields.restangularCollection]) {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this,
- processedData,
- what,
- true,
- fullParams
- ),
- filledArray
- );
- } else {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this[config.restangularFields.parentResource],
- processedData,
- __this[config.restangularFields.route],
- true,
- fullParams
- ),
- filledArray
- );
- }
- };
- urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
- this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
- if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
- resolvePromise(deferred, response, __this, filledArray);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
- deferred.reject(response);
- }
- });
- return restangularizePromise(deferred.promise, true, filledArray);
- }
- function withHttpConfig(httpConfig) {
- this[config.restangularFields.httpConfig] = httpConfig;
- return this;
- }
- function save(params, headers) {
- if (this[config.restangularFields.fromServer]) {
- return this[config.restangularFields.put](params, headers);
- } else {
- return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
- }
- }
- function elemFunction(operation, what, params, obj, headers) {
- var __this = this;
- var deferred = $q.defer();
- var resParams = params || {};
- var route = what || this[config.restangularFields.route];
- var fetchUrl = urlHandler.fetchUrl(this, what);
- var callObj = obj || this;
- // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
- var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
- if (_.isObject(callObj) && config.isRestangularized(callObj)) {
- callObj = stripRestangular(callObj);
- }
- var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
- headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
- var filledObject = {};
- filledObject = config.transformElem(filledObject, false, route, service);
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
- if (elem) {
- if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
- var data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- route,
- true,
- null,
- fullParams
- );
- resolvePromise(deferred, response, data, filledObject);
- } else {
- var data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- __this[config.restangularFields.route],
- true,
- null,
- fullParams
- );
- data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
- resolvePromise(deferred, response, data, filledObject);
- }
- } else {
- resolvePromise(deferred, response, undefined, filledObject);
- }
- };
- var errorCallback = function(response) {
- if (response.status === 304 && config.isSafe(operation)) {
- resolvePromise(deferred, response, __this, filledObject);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
- deferred.reject(response);
- }
- };
- // Overriding HTTP Method
- var callOperation = operation;
- var callHeaders = _.extend({}, request.headers);
- var isOverrideOperation = config.isOverridenMethod(operation);
- if (isOverrideOperation) {
- callOperation = 'post';
- callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()});
- } else if (config.jsonp && callOperation === 'get') {
- callOperation = 'jsonp';
- }
- if (config.isSafe(operation)) {
- if (isOverrideOperation) {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
- } else {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
- }
- } else {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
- }
- return restangularizePromise(deferred.promise, false, filledObject);
- }
- function getFunction(params, headers) {
- return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
- }
- function deleteFunction(params, headers) {
- return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
- }
- function putFunction(params, headers) {
- return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
- }
- function postFunction(what, elem, params, headers) {
- return _.bind(elemFunction, this)('post', what, params, elem, headers);
- }
- function headFunction(params, headers) {
- return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
- }
- function traceFunction(params, headers) {
- return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
- }
- function optionsFunction(params, headers) {
- return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
- }
- function patchFunction(elem, params, headers) {
- return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
- }
- function customFunction(operation, path, params, headers, elem) {
- return _.bind(elemFunction, this)(operation, path, params, elem, headers);
- }
- function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
- var bindedFunction;
- if (operation === 'getList') {
- bindedFunction = _.bind(fetchFunction, this, path);
- } else {
- bindedFunction = _.bind(customFunction, this, operation, path);
- }
- var createdFunction = function(params, headers, elem) {
- var callParams = _.defaults({
- params: params,
- headers: headers,
- elem: elem
- }, {
- params: defaultParams,
- headers: defaultHeaders,
- elem: defaultElem
- });
- return bindedFunction(callParams.params, callParams.headers, callParams.elem);
- };
- if (config.isSafe(operation)) {
- this[name] = createdFunction;
- } else {
- this[name] = function(elem, params, headers) {
- return createdFunction(params, headers, elem);
- };
- }
- }
- function withConfigurationFunction(configurer) {
- var newConfig = angular.copy(_.omit(config, 'configuration'));
- Configurer.init(newConfig, newConfig);
- configurer(newConfig);
- return createServiceForConfiguration(newConfig);
- }
- function toService(route, parent) {
- var knownCollectionMethods = _.values(config.restangularFields);
- var serv = {};
- var collection = (parent || service).all(route);
- serv.one = _.bind(one, (parent || service), parent, route);
- serv.post = _.bind(collection.post, collection);
- serv.getList = _.bind(collection.getList, collection);
- for (var prop in collection) {
- if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.contains(knownCollectionMethods, prop)) {
- serv[prop] = _.bind(collection[prop], collection);
- }
- }
- return serv;
- }
- Configurer.init(service, config);
- service.copy = _.bind(copyRestangularizedElement, service);
- service.service = _.bind(toService, service);
- service.withConfig = _.bind(withConfigurationFunction, service);
- service.one = _.bind(one, service, null);
- service.all = _.bind(all, service, null);
- service.several = _.bind(several, service, null);
- service.oneUrl = _.bind(oneUrl, service, null);
- service.allUrl = _.bind(allUrl, service, null);
- service.stripRestangular = _.bind(stripRestangular, service);
- service.restangularizeElement = _.bind(restangularizeElem, service);
- service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
- return service;
- }
- return createServiceForConfiguration(globalConfiguration);
- }];
- });
- })();
|