From 69ff9397d3593451584bf506e81cc30d4660979f Mon Sep 17 00:00:00 2001 From: Pedro Marce Date: Sun, 18 Jan 2015 17:55:53 +0000 Subject: [PATCH] Add getFieldId restangular method, update README --- README.md | 6 +- dist/restangular.js | 2540 ++++++++++++++++++++------------------- dist/restangular.min.js | 4 +- dist/restangular.zip | Bin 75378 -> 67263 bytes src/restangular.js | 8 +- 5 files changed, 1298 insertions(+), 1260 deletions(-) diff --git a/README.md b/README.md index f750a05a..e85607de 100644 --- a/README.md +++ b/README.md @@ -1095,12 +1095,12 @@ RestangularProvider.setRestangularFields({ #### **What if each of my models has a different ID name like CustomerID for Customer** -In some cases, peolpe have different ID name for each entity. For example, they have CustomerID for customer and EquipmentID for Equipment. If that's the case, you can override's Restangular's getIdFromElem. For that, you need to do: +In some cases, peolpe have different ID name for each entity. For example, they have CustomerID for customer and EquipmentID for Equipment. If that's the case, you can override's Restangular's getFieldId. For that, you need to do: ````js -RestangularProvider.configuration.getIdFromElem = function(elem) { +RestangularProvider.configuration.getFieldId = function(elem) { // if route is customers ==> returns customerID - return elem[_.initial(elem.route).join('') + "ID"]; + return _.initial(elem.route).join('') + "ID"; } ```` diff --git a/dist/restangular.js b/dist/restangular.js index 47e53395..e7eb8668 100644 --- a/dist/restangular.js +++ b/dist/restangular.js @@ -1,6 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.4.0 - 2014-04-25 * @link https://github.com/mgonto/restangular + * @version v1.4.0 - 2015-01-18 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT */(function() { @@ -8,1298 +8,1332 @@ var module = angular.module('restangular', []); module.provider('Restangular', function() { - // Configuration - var Configurer = {}; - Configurer.init = function(object, config) { - /** - * Those are HTTP safe methods for which there is no need to pass any data with the request. - */ - - object.configuration = config; - - 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' - }; - object.setRestangularFields = function(resFields) { - config.restangularFields = - _.extend(config.restangularFields, resFields); - return this; - }; - - config.isRestangularized = function(obj) { - return !!obj[config.restangularFields.one] || !!obj[config.restangularFields.all]; - }; - - 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) { - 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; - }; - - object.setResponseInterceptor = object.addResponseInterceptor; - object.setResponseExtractor = object.addResponseInterceptor; - - /** - * 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 - }; - }; + // 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.getFieldId = function(elem) { + return config.restangularFields.id; + }; + + config.setIdToElem = function(elem, id /*, route */) { + config.setFieldToElem(config.getFieldId(elem), elem, id); + return this; + }; + + config.getIdFromElem = function(elem) { + return config.getFieldFromElem(config.getFieldId(elem), 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.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]; - 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.errorInterceptor = config.errorInterceptor || function() {}; - - object.setErrorInterceptor = function(interceptor) { - config.errorInterceptor = interceptor; - return this; - }; - - config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) { - return elem; - }; - object.setOnBeforeElemRestangularized = function(post) { - config.onBeforeElemRestangularized = post; - 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; + 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); } - config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse; - object.setFullResponse = function(full) { - config.fullResponse = full; - return this; - }; + if (config.isValidId(elemId) && !elem.singleOne) { + elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId); + } + } + } + + return acum.replace(/\/$/, '') + '/' + elemUrl; + + }, 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; } + + 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); - //Internal values and functions - config.urlCreatorFactory = {}; - /** - * Base URL Creator. Base prototype for everything related to it - **/ - var BaseCreator = function() { - }; + this.$get = ['$http', '$q', function($http, $q) { - BaseCreator.prototype.setConfig = function(config) { - this.config = config; - return this; - }; + function createServiceForConfiguration(config) { + var service = {}; - BaseCreator.prototype.parentsArray = function(current) { - var parents = []; - while(current) { - parents.push(current); - current = current[this.config.restangularFields.parentResource]; - } - return parents.reverse(); - }; + var urlHandler = new config.urlCreatorFactory[config.urlCreator](); + urlHandler.setConfig(config); - function RestangularResource(config, $http, url, configurer) { - var resource = {}; - _.each(_.keys(configurer), function(key) { - var value = configurer[key]; + 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); - // 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; - } + // Tag element as restangularized + elem[config.restangularFields.restangularized] = true; - if (config.isSafe(value.method)) { + // 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); - resource[key] = function() { - return $http(_.extend(value, { - url: url - })); - }; + elem[config.restangularFields.fromServer] = !!fromServer; - } else { + if (parent && config.shouldSaveParent(route)) { + var parentId = config.getIdFromElem(parent); + var parentUrl = config.getUrlFromElem(parent); - resource[key] = function(data) { - return $http(_.extend(value, { - url: url, - data: data - })); - }; + 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); + } - return resource; + elem[config.restangularFields.parentResource] = parentResource; + } else { + elem[config.restangularFields.parentResource] = null; + } + return elem; + } + + function one(parent, route, id, singleOne) { + if (_.isNumber(route) || _.isNumber(parent)) { + var 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); + } + 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 + ); + } + }; - 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.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); - } - } - } - - return acum.replace(/\/$/, "") + "/" + elemUrl; - - }, 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; - 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; + 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]) { + resolvePromise(deferred, response, restangularizeElem(__this, elem, what, true, null, fullParams), 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 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); - - // 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); - } - if (config.isValidId(parentUrl)) { - config.setUrlToElem(parentResource, parentUrl); - } - - elem[config.restangularFields.parentResource] = parentResource; - } else { - elem[config.restangularFields.parentResource] = null; - } - return elem; - } - - - - function one(parent, route, id, singleOne) { - if (_.isNumber(route) || _.isNumber(parent)) { - var error = "You're creating a Restangular entity with the number " - error += "instead of the route or the parent. You can't call .one(12)"; - throw new Error(error); - } - var elem = {}; - config.setIdToElem(elem, id); - 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; - 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(stripRestangular(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"; - } - - urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what, - this[config.restangularFields.etag], operation)[method]().then(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); - } - }, function error(response) { - if (response.status === 304 && __this[config.restangularFields.restangularCollection]) { - resolvePromise(deferred, response, __this, filledArray); - } else if ( config.errorInterceptor(response, deferred) !== 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]) { - resolvePromise(deferred, response, restangularizeElem(__this, elem, what, true, null, fullParams), filledObject); - } else { - 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 ( config.errorInterceptor(response, deferred) !== false ) { - deferred.reject(response); - } - }; - // Overring 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}); - } 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 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); - return serv; - } + 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); + } + }; + // Overring 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}); + } 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); + } + } - Configurer.init(service, config); - - service.copy = _.bind(copyRestangularizedElement, service); + return serv; + } - service.service = _.bind(toService, service); - service.withConfig = _.bind(withConfigurationFunction, service); + Configurer.init(service, config); - service.one = _.bind(one, service, null); + service.copy = _.bind(copyRestangularizedElement, service); - service.all = _.bind(all, service, null); + service.service = _.bind(toService, service); - service.several = _.bind(several, service, null); + service.withConfig = _.bind(withConfigurationFunction, service); - service.oneUrl = _.bind(oneUrl, service, null); + service.one = _.bind(one, service, null); - service.allUrl = _.bind(allUrl, service, null); + service.all = _.bind(all, service, null); - service.stripRestangular = _.bind(stripRestangular, service); + service.several = _.bind(several, service, null); - service.restangularizeElement = _.bind(restangularizeElem, service); + service.oneUrl = _.bind(oneUrl, service, null); - service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service); + service.allUrl = _.bind(allUrl, service, null); - return service; - } + service.stripRestangular = _.bind(stripRestangular, service); + + service.restangularizeElement = _.bind(restangularizeElem, service); - return createServiceForConfiguration(globalConfiguration); + service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service); - }]; + return service; } -); + + return createServiceForConfiguration(globalConfiguration); + }]; +}); })(); diff --git a/dist/restangular.min.js b/dist/restangular.min.js index 4db0ed62..845ca02f 100644 --- a/dist/restangular.min.js +++ b/dist/restangular.min.js @@ -1,6 +1,6 @@ /** * Restful Resources service for AngularJS apps - * @version v1.4.0 - 2014-04-25 * @link https://github.com/mgonto/restangular + * @version v1.4.0 - 2015-01-18 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT - */!function(){var a=angular.module("restangular",[]);a.provider("Restangular",function(){var a={};a.init=function(a,b){function c(a,b,c,d){var e={};return _.each(_.keys(d),function(f){var g=d[f];g.params=_.extend({},g.params,a.defaultRequestParams[g.method.toLowerCase()]),_.isEmpty(g.params)&&delete g.params,e[f]=a.isSafe(g.method)?function(){return b(_.extend(g,{url:c}))}:function(a){return b(_.extend(g,{url:c,data:a}))}}),e}a.configuration=b;var d=["get","head","options","trace","getlist"];b.isSafe=function(a){return _.contains(d,a.toLowerCase())};var e=/^https?:\/\//i;b.isAbsoluteUrl=function(a){return _.isUndefined(b.absoluteUrl)||_.isNull(b.absoluteUrl)?a&&e.test(a):b.absoluteUrl},b.absoluteUrl=_.isUndefined(b.absoluteUrl)?!0:b.absoluteUrl,a.setSelfLinkAbsoluteUrl=function(a){b.absoluteUrl=a},b.baseUrl=_.isUndefined(b.baseUrl)?"":b.baseUrl,a.setBaseUrl=function(a){return b.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a,this},b.extraFields=b.extraFields||[],a.setExtraFields=function(a){return b.extraFields=a,this},b.defaultHttpFields=b.defaultHttpFields||{},a.setDefaultHttpFields=function(a){return b.defaultHttpFields=a,this},b.withHttpValues=function(a,c){return _.defaults(c,a,b.defaultHttpFields)},b.encodeIds=_.isUndefined(b.encodeIds)?!0:b.encodeIds,a.setEncodeIds=function(a){b.encodeIds=a},b.defaultRequestParams=b.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},a.setDefaultRequestParams=function(a,c){var d=[],e=c||a;return _.isUndefined(c)?d.push("common"):_.isArray(a)?d=a:d.push(a),_.each(d,function(a){b.defaultRequestParams[a]=e}),this},a.requestParams=b.defaultRequestParams,b.defaultHeaders=b.defaultHeaders||{},a.setDefaultHeaders=function(c){return b.defaultHeaders=c,a.defaultHeaders=b.defaultHeaders,this},a.defaultHeaders=b.defaultHeaders,b.methodOverriders=b.methodOverriders||[],a.setMethodOverriders=function(a){var c=_.extend([],a);return b.isOverridenMethod("delete",c)&&c.push("remove"),b.methodOverriders=c,this},b.jsonp=_.isUndefined(b.jsonp)?!1:b.jsonp,a.setJsonp=function(a){b.jsonp=a},b.isOverridenMethod=function(a,c){var d=c||b.methodOverriders;return!_.isUndefined(_.find(d,function(b){return b.toLowerCase()===a.toLowerCase()}))},b.urlCreator=b.urlCreator||"path",a.setUrlCreator=function(a){if(!_.has(b.urlCreatorFactory,a))throw new Error("URL Path selected isn't valid");return b.urlCreator=a,this},b.restangularFields=b.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"},a.setRestangularFields=function(a){return b.restangularFields=_.extend(b.restangularFields,a),this},b.isRestangularized=function(a){return!!a[b.restangularFields.one]||!!a[b.restangularFields.all]},b.setFieldToElem=function(a,b,c){var d=a.split("."),e=b;return _.each(_.initial(d),function(a){e[a]={},e=e[a]}),e[_.last(d)]=c,this},b.getFieldFromElem=function(a,b){var c=a.split("."),d=b;return _.each(c,function(a){d&&(d=d[a])}),angular.copy(d)},b.setIdToElem=function(a,c){return b.setFieldToElem(b.restangularFields.id,a,c),this},b.getIdFromElem=function(a){return b.getFieldFromElem(b.restangularFields.id,a)},b.isValidId=function(a){return""!==a&&!_.isUndefined(a)&&!_.isNull(a)},b.setUrlToElem=function(a,c){return b.setFieldToElem(b.restangularFields.selfLink,a,c),this},b.getUrlFromElem=function(a){return b.getFieldFromElem(b.restangularFields.selfLink,a)},b.useCannonicalId=_.isUndefined(b.useCannonicalId)?!1:b.useCannonicalId,a.setUseCannonicalId=function(a){return b.useCannonicalId=a,this},b.getCannonicalIdFromElem=function(a){var c=a[b.restangularFields.cannonicalId],d=b.isValidId(c)?c:b.getIdFromElem(a);return d},b.responseInterceptors=b.responseInterceptors||[],b.defaultResponseInterceptor=function(a){return a},b.responseExtractor=function(a,c,d,e,f,g){var h=angular.copy(b.responseInterceptors);h.push(b.defaultResponseInterceptor);var i=a;return _.each(h,function(a){i=a(i,c,d,e,f,g)}),i},a.addResponseInterceptor=function(a){return b.responseInterceptors.push(a),this},a.setResponseInterceptor=a.addResponseInterceptor,a.setResponseExtractor=a.addResponseInterceptor,b.requestInterceptors=b.requestInterceptors||[],b.defaultInterceptor=function(a,b,c,d,e,f,g){return{element:a,headers:e,params:f,httpConfig:g}},b.fullRequestInterceptor=function(a,c,d,e,f,g,h){var i=angular.copy(b.requestInterceptors),j=b.defaultInterceptor(a,c,d,e,f,g,h);return _.reduce(i,function(a,b){return _.extend(a,b(a.element,c,d,e,a.headers,a.params,a.httpConfig))},j)},a.addRequestInterceptor=function(a){return b.requestInterceptors.push(function(b,c,d,e,f,g,h){return{headers:f,params:g,element:a(b,c,d,e),httpConfig:h}}),this},a.setRequestInterceptor=a.addRequestInterceptor,a.addFullRequestInterceptor=function(a){return b.requestInterceptors.push(a),this},a.setFullRequestInterceptor=a.addFullRequestInterceptor,b.errorInterceptor=b.errorInterceptor||function(){},a.setErrorInterceptor=function(a){return b.errorInterceptor=a,this},b.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){return b.onBeforeElemRestangularized=a,this},b.onElemRestangularized=b.onElemRestangularized||function(a){return a},a.setOnElemRestangularized=function(a){return b.onElemRestangularized=a,this},b.shouldSaveParent=b.shouldSaveParent||function(){return!0},a.setParentless=function(a){return _.isArray(a)?b.shouldSaveParent=function(b){return!_.contains(a,b)}:_.isBoolean(a)&&(b.shouldSaveParent=function(){return!a}),this},b.suffix=_.isUndefined(b.suffix)?null:b.suffix,a.setRequestSuffix=function(a){return b.suffix=a,this},b.transformers=b.transformers||{},a.addElementTransformer=function(c,d,e){var f=null,g=null;2===arguments.length?g=d:(g=e,f=d);var h=b.transformers[c];return h||(h=b.transformers[c]=[]),h.push(function(a,b){return _.isNull(f)||a==f?g(b):b}),a},a.extendCollection=function(b,c){return a.addElementTransformer(b,!0,c)},a.extendModel=function(b,c){return a.addElementTransformer(b,!1,c)},b.transformElem=function(a,c,d,e,f){if(!f&&!b.transformLocalElements&&!a[b.restangularFields.fromServer])return a;var g=b.transformers[d],h=a;return g&&_.each(g,function(a){h=a(c,h)}),b.onElemRestangularized(h,c,d,e)},b.transformLocalElements=_.isUndefined(b.transformLocalElements)?!1:b.transformLocalElements,a.setTransformOnlyServerElements=function(a){b.transformLocalElements=!a},b.fullResponse=_.isUndefined(b.fullResponse)?!1:b.fullResponse,a.setFullResponse=function(a){return b.fullResponse=a,this},b.urlCreatorFactory={};var f=function(){};f.prototype.setConfig=function(a){return this.config=a,this},f.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},f.prototype.resource=function(a,d,e,f,g,h,i,j){var k=_.defaults(g||{},this.config.defaultRequestParams.common),l=_.defaults(f||{},this.config.defaultHeaders);i&&(b.isSafe(j)?l["If-None-Match"]=i:l["If-Match"]=i);var m=this.base(a);if(h){var n="";/\/$/.test(m)||(n+="/"),n+=h,m+=n}return this.config.suffix&&-1===m.indexOf(this.config.suffix,m.length-this.config.suffix.length)&&!this.config.getUrlFromElem(a)&&(m+=this.config.suffix),a[this.config.restangularFields.httpConfig]=void 0,c(this.config,d,m,{getList:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),get:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),jsonp:this.config.withHttpValues(e,{method:"jsonp",params:k,headers:l}),put:this.config.withHttpValues(e,{method:"PUT",params:k,headers:l}),post:this.config.withHttpValues(e,{method:"POST",params:k,headers:l}),remove:this.config.withHttpValues(e,{method:"DELETE",params:k,headers:l}),head:this.config.withHttpValues(e,{method:"HEAD",params:k,headers:l}),trace:this.config.withHttpValues(e,{method:"TRACE",params:k,headers:l}),options:this.config.withHttpValues(e,{method:"OPTIONS",params:k,headers:l}),patch:this.config.withHttpValues(e,{method:"PATCH",params:k,headers:l})})};var g=function(){};g.prototype=new f,g.prototype.base=function(a){var c=this;return _.reduce(this.parentsArray(a),function(a,d){var e,f=c.config.getUrlFromElem(d);if(f){if(c.config.isAbsoluteUrl(f))return f;e=f}else if(e=d[c.config.restangularFields.route],d[c.config.restangularFields.restangularCollection]){var g=d[c.config.restangularFields.ids];g&&(e+="/"+g.join(","))}else{var h;h=c.config.useCannonicalId?c.config.getCannonicalIdFromElem(d):c.config.getIdFromElem(d),b.isValidId(h)&&!d.singleOne&&(e+="/"+(c.config.encodeIds?encodeURIComponent(h):h))}return a.replace(/\/$/,"")+"/"+e},this.config.baseUrl)},g.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},g.prototype.fetchRequestedUrl=function(a,c){function d(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b.sort()}function e(a,b,c){for(var e=d(a),f=0;fqL>%_9>gu&=@;QA=XB&zcrk zn4O!KomYaq_xGYvV$LsQl*QX1`*biwwlQ{xWGfpNx}i^^Zq@e=TPC zmjM2PjDU<0&e&5oDe$Gg&X<@&MnGY-MkAC7oqU0Y4wKU|+tEuiE(<8cGXWZ@P|15z zXIu@)jupy0uY%38tj0^;9~K+JX7FniZDclMk6mY0Y6kLU<+0H3`-$@Ar{YvDt(7(XTA%~$34Me*eVM}ZCJeDux>2t zyTVHGQJ9eyScpquaaa}}jz5RzjIQlUc!{Vx6%k#cJie7HaUmiN|B5JrQJxKld=j3O zM<_Jn5;QbElei!dBVWt&nPf#lOpR>9p2#fHjYJXfugEGiDvEH6!iZNCrP0GWci(Zl zRuZ*XoOt`xXuKK~7o_Po4fJSe6@QA#M|N}(8rDZhH2Czh=$HkX0kVT$t9dLJ(j7}< z@kVs`&-XYnW|0q>#g>>!X8L&JJ@&tV@N zpxx5&)(h=7IV`q;HjC3f=;&nV@c^R3@wjB-^>keHT5qpTlg(zgSvyU=Q`jsE7mfvZ zsVoDgZZ@ICIbgDN5B8cI^#t*votECNX;#}3BI$8hy1a0%b)spzw}l|n$cEPvrTfuK z+&@SvsiEId2-i{#Q&um`qpF?__sE1f4mLgaAMVMx=OCclgSes0wX7xILI|&yaieH& zJ9`)tto2SwjFMD63ny23N< zH<%vSATi2R)F@-np;Y2FrGTUK{aC3*M2a%Kzt?0X52WF(cMzwEIVufn2)5&as3f-S zV-(Z6(qbkr&0GLk>FSh8crhgnS5pc{eZg#@5IY1l_8yUiGxGK^2Sxvyc*Ll*Xi({} zOqGCpRK<8#m4p>(l%evS|6I`FCP22^G?Fi+~rH2cvJN@r-+RP1kw zz+d%ptezO6Fq;NUTz>X`ix|M@aXecY=#k{dS?;4XSYJYj6kjIZ_4=uJTVH|1>;zP1 z7h+BJM9k4E@yUrGQ7`_Uo#54piX0`I39}6fD6$gq>9+wPmgUS14cHyywnA2t=<5`W z2rG$0Q&zBo?X$gOv88jsU^1KQ9S*w#ODF0PUJ{FsiV{cVdd|>@3x-JKdeifzC`{=c7Q8WVnS{mB&CaP>9?w&mo8GRcgJ?)N!u{KVNeQ3-2qCeG)_^93mK z75Fh#!);4m zbj2kj)z zRYl_sqB&ldgsAEqG*_o!s9K5ps?~_83B$+La>TwMr)L2*H3EJl7vWjI<_Chn6@eIdPM zVR3zg2)LtuKK@kSh4?xJ8*_6*CEjjO;#z|MU1K?xHckp*b8zivlKY^s2qX2Xab9TQ zdH3uu`VDo$vdvIrNamIHI6nVqbD`e1FXaJX>nLOEgn0krQr0m zMz$1XbGnG1(>w(So8xh|xd8qxY81Al!qlR}I$07TGQ&UU=%&*=w)39=YdhyA@;C#VQYVMm|E=} zfr#n{=!(|4C5XDoOPxHdb4$Tv4Q@Z}Nr2};vCxdQ3$w&z=Y@rW zsC<81n2o2q!cnwHh3*IAv0>4)P?qYmuz~_Y(*ExkHDaWnA6ESh#($f5wnX;^Hx+O!%yOP=uLVLA?J zglmg}c9m_ARmWT!P0buL^lohAk_lXtn6sfW9;!?zOby#NiM= zF|!F{S7NY6qoau!*3_lIny%ykZnRQ7)int}bZMC?rnu96w;B@tSal<-T+^K(3g)S< zxVus$Avo8ahHKph6!uhOUC$((?I}m7wG^$^8tkU;7uHJTEl$Hti(|2Nu?mM4XW^5@ zTqMlO)0U)ija#s!68o2=d{E?Ns8fqyC~xAQzuzthT7!`=w4oiOj75O<=MRb z`|?~Atti6s6=t+hUF-6S7++=~!pb=)w?rdnRRH#^q$n*d66aRNOWj!kk?fcqXunHz?@PSnRoR+rMlXvmN_O^ZTxVAO~@^zVLUPoKKZYrn!13jm&j}xmoE`6}1G;Fkr!`II^ zxnWflF0W7g`P5xE|7YE`IPr_5D9_2X;=pYNe0W#Tw)gt#6OZoOD!A>rn3&PC5=6|gx0Bb&4F-DX+6TVPga z+j!#^J)=&j6a?ph!`eRv?Ln1^K9H^Tsm3@|ZlN_$A@qaa2ICi78pZ`X-)+g~hWG~MP&|N<>HkkR_r#@5j$Yf;Hf*5Fouy=b6 zjyHygA}9YtY=xm=2X|Jk!(rqI_wFd>oA%X?X{bVtSLY94GX91pj$^tinqUYX+ZT#Y zcge8(P8D9hb2>V9x1#?p62x6vB;K9o`Y>D)iqG%T^B^^UXA%QqcxNV$8cytt!Et{X z#~pK}kxPw{l3(HRI1{eRfGhWhVY^F-W3C%9_uh0=-fiG*#hwhDx?4u@G~BZ(9`wCd=x?e5{f^csRb(n)=_ckM9mj>0lJc}?9!^NoH9ZO8#$UD;fxVBsFo}h@s zt~WSi&(vlaOCcrKbB(_r0W1;myNuRJyjnU9-s@8gMTx{YDM3Ab}fF+5I+ zwY-o+mq+d5VHa~a9evTP8h^v#CO@_!b_8BHY()N3%{z(%v29%#sVk}I6B=e5qo6{3 z1UcD+AG)8c<&opr8R7Wm$ujEMge}j9lRa!Z9h2ZGHhY+$aG;+m$rj3^{>N-*E6`%rzc|Q=_*S572KwO|8x$Lo+-zx z;W8|Krqq3Gqy~t`0H&YKA9s4xjx>9%@S71elAf(W&$FfE{?qX8v+L1$tVTxW#?KHs zupm66B=H5{tz(h6d~7P}pUY-e{mbV%@%?l2G4FT^esO%}IJi6m&?XTnJdmXu`?uvps1dq zNM?1G>l*-bD~})4Bln~fp(nGMCp>>L56e%gC^Zb_86;B{pHu04W0;ODI8{K3)X0@c zep>baR*8O-C&S-P3rIbq!OSzI*mxIQaq}W-e&Z%*cXnRrH@~T6OBguN zeMjeofB9+|g9m%B=)9PnznvuZ5HINn9*y|dV)!IgueIWy*JkkNf4sH=!>=ddf!B2? zeIpLvy)IUwO5d2w7k=LxHTe3COssBC!h$@yyE+$*o#*I=pgjmD&Lt8o7~fJ6I_7*5 zMM`0qbN&YIq)5;^-c)mgZGN+yM=!hI%xBTdIExK@s}j@SYNV7k0#Ce^f_LaU|AKzX|+>JS3z++czFnN?HJmO=uqZTj2o$>avpR4mCfyRdR?G@KGZ=bI&gsi+lyK1P$G{4f z3H0lz97Ypboef^M>E&oXBWULos)Ys$mIvrs&Z)gV-+Df^Zk!XvUXB-&U_2YW}4rt)X@_~54;XNtkcrzIsSh!bt`T< zzi0LQ5#~tV`=5uZ^mO`rE`?b!pe*$#0K|T=pA`rE7#1IWaV=`}!p-PC zpiurrlCQyP#>Fp7LPrU-CLL10sv8eEvhu4z-=Yo!PprqJb57%RV5?WpZ`YefuaOR4 zQQ?KI8y)JAAIoO)khOJW!t#blKSwglEYWh@YW^; zwp@)n()PzwK1bx=UkV8^n$+qcyVWe@s*EH5lt`}mFgXMcH-s3XKCDH5@bUm0X(Ps7N2X(_dHnM9M>pS&MtySNxx9C;5dREracj<_r zoD;aNBug5S{x`;<`bZIO(st?>>xVq7jZatW~0YfVbK>+Yjt{yk^X7a zWz=@AUOioBpg@ZRT5e$#9-`#7->qjjV$DR<1{St>FkSqOv1l>4vxiZX8$j4{@^#$R z^)NrzrR(y#wdroZ)q>^CKw!g+I+Y0%V!gw#hi6AzwQ7kj K;EKP5-u?#?b{QZ5 delta 11840 zcmbU{30Rv|wuQ0=NLWHx3x84w34{bFl(GZ}l^?$#JMO^e-X!`9h}J9lB%?Xou7vBN;xTf5wv zB}HJhDdDPbDs1bCfaI~cpea{E-B=Se*%a{khhgx6Uno2>Hk{^P7+V8@ex*?2#}Qnc zEd*}*$ziQu1;78l`4#h>D}Gw|q$ehH2>ojRVu|K-6YadhKYg4=!xeIkBqn2nwaY%= z)h^0{vGjz0UH~r*SneMUSNv5<35h5rjw^-mfJ|x%%hFC!{BFq;WP@yob60xcmTh3NvazXV3YTY*|0 z+10?JKmh>W4vdG&AQbZutbihZW~v-E2c^>yJQGwRiGFPGOdipU;0b)EH#k+AJr-P2 zBW}-e?8FP1k{L#bsMFc?b2Hr|CLlr<8{XT_;uS}2K=VS)>NS3iDL<5_TjssIj3cOUKgl$LTpoNXiKN1bQLb7-^jsz#e`yp{) z=~cpxo-l7QLu6%hPzzr{k7LAbRrO*j+LnJWd@Y}+g zMjY7nun4#kmday~kQXB6O8EqMt2P?8%Cq65JT^?@vUfOp>>54l4!lqkJC=zEu7<_I zKjnmuTV>8w^<$m7ZfDIuNB3FA}4$zwa)Myh$@c(UAcUu z`aToh1|LGuSW(>$nq^QB5gV@Qw7Od~9@U2Wh#cH=YeX^ZYKn)iUQ~}o+G~%*!ESSO zFf&LFYlpo^1{-_Wo%^L5Qx>^AQq%}9TlHY1hNSR9WFEC&M_NV$%Zakn^q#01>0E@X za@iCDr>6vfk1}n8xBG~oxH#u>cDwCtlG;(`JFPCe!#&5|Yo zD1cq|_NG~_jui~J#bs~eA-v?kxTY)3Tl*1ovAJ2yz_ZmtZ%@*TMnH6TjNsf(#H916FNjlL#~ApL=vcwdm&PV) zJ=@xxHPv(Hv5oo8(7iiiHT>cov59==aBK<`>FwrP@7`W7KL$UFI9!_s3{#G+{G{+Y*-sXulOfm{-UQSWMU0N9&9IvME`&e)= zyutddDWM@=G{N3f9rX3Y2idG{t4Ij3Gc6@^IAEA`yvPbvG3WY9N&+vZ4^qncPE4wB zv9nW)PzbivVzx;Zl=iAf=6_BNBfa?|wJb=G2nWxnBtV5WcEo|@rA_6ru1d4~La=o! zZDy*73zHcy28YGub-Fp;X^*&wgX6ks{`3S_bS#d^2!XvB3YerP&GSRGhL!zFaOqWW z@}v&-p432kW-8_-7(J21w(V3wk|7in=~3PYcm}G{r^9^@DB%9|4yMad5Rp*=*Xlx{ zHe)*UXUyS&KFmlW+5D7Y;RlZElAzu&2jvnZU9ICM(`o;;{W7>_m;}1aG@@f_uY5#= z+nYH>N((xSYDS;hgE`j7Ujy$e;$v3AB4Z)x%vx#AZ>*Sk95gPX^Zm&vBXSj{Ql6CEMm=204jq2@Bd>!wt9mr` zzb9)6-Hy+)v~l8rdZI@f5yHT8*|AWNoj-((?b!vWroYY}%_fGzZ*w9cImd|hC&2Au zcb~WB_8h_O?8-^wxj30q0^in!!&f(G40lT-iFD-^48^B}wZ*~U&X0o+@@ccxfAo0;e9n3uv13~#E{Nl2FLH5h?Ei_}zSHW}n0>(e(8-uyl zt_o*+yS))C1JUq(ogBtbqO6MU-gTpbm=OtFhfa#6^)jAP$sBH)R7ff_u^Arn#Vui7p}_b3g+j#fTww*N^EZXl zP%+VNVseYQ>)aweC=W;Yuw=MMCwO1nHV$4dG6r!?*7hztR|xWA&SP3l#g&N0Q9Ob2 z;&xjw6q^uqUmx9b|a zU6_1%+&vEf@uh+`6qHs2Mk*+=eWfw9;ghA)Q3~IcCSxNnqdQ+(X2d6ZSvoe`%Cci5 zCOw;C53q>vZdn?F{iCb|8@(kPYAk7_c9_5KvZzVFX0db{Z~8ZNk??^f13|A@#$%%> zPrwF)nvF-Jpp->0sF1VDGsr9mzYoj9)B)y`#@IjfDr9R|wE1KA!j z+%2tLMxk!k)!o$8y2^`w5sOv}?^jljc1T(+r-t9mPJr4eDp)clgN|kAlqnRbU7V87 z(;7B)DiZm-nMznXH5~exA74LJP?|$iD}HuPKaH*su3I%Na_G9}rb+7prVqneIeoH- z6Q)Oq>+sb5Fw=$F>666y@iU^t`5`6=b6#(_!5I>s2vIE9Qu6B!SUz(Yp_^w4%JWJ#-LSHdM`=nl*dqxRHn;n*(DrYrHo?5T8NDyqlfGStfM z{%}}bokUuR&%OO}c($7E=a<#>P*76^Yid#vOvjRM)IW_$!)35^q|4amY<1`~2ENcH z63Xy0TP>i+=28t6&aTyzQutR?E4UuE8%|?LSX8TmCu&LknNIWUov*FJIhkiy(9WsB z73d1^9~~LSfBWaeaT<-D5yg{+`4U^@6=AVO=jM!8i6rvoSbT!3a|DX?bA?2e-AFH+ z$#Fs%+{1lui`9+3O`wL%3-pk(AP=e+q>!0F-}XbD40bHwh4S!%Qk2sN z3k=xYT2KU8brynTPzraq%fYR%ukI2Updw=(A4*@S%Vr|YW&cW;xRBXh)-GR|OzjTg zd2pc-MX%@OhXzp1N_WQf~0!NXKL$}CeG2_-i}c*rGuQ= z%ym0#_NG>c-6rB0f}_7aHBkWN7Ey-<0n+VYY@V;rhimoOkbGMPu{!IvtYoiwo|K`# z(0AK7x}8|q#tOmHw>5-`AfzOe2Ocy>K>i|ml!OhZ1lDFQ8V@CBRIq+gasWRDhZYqG zNQ8hNR-WbH=Av*sjKIZog4|-#9_yXv_~A0z#$$0p{7_1@wDXBtoD~70!&nP*7BLGR@3KWS8s>@Jtng_Z+e z`z$r3-r3Eunsl0*{WASGvU-#*J4f&JE`Z_O)s#*o{FS9Q=bIF8xp5r)cT> zH>=^x7XHOhR4cu$SlNc8#>0DGm?d9_c3B{sNoUUrb|?Q8iIr`+D9HZ{HZ3e;M~r4~ z!wMBH-nC*bHt(;X*MW+5I-2tKg*dpUT?ePzCn{JOxU0qEt4MFIx0{H-M>~`xp5Jw3 zP>JEPlblh21H;-qo#Z@|I?0^2IA$Q$#~k$ZM+a$oS!V(SIYV)R+nyk2_Q^}}$i#?Z zIKCnXmOjefI(E(wc2vRzr_yWJq1%;p(gqAGtnXC)BC4d7mBF5qZ(M1F9V-jr*DEv0 zIb2?u4^NdRLo~|`wjND@l{6fD^sAGlKB zJ6Aqvx}sCM+^*J6uj0Or47A=$223!Rg<-V5w@XQ{9MNOF&{ZYnwmbp!&MVF>TAsOC zqAGLTGNawv*rHp+zawE~eR>;0FA?x1x{djLw_O9TxRW%VMPAEnP6TW0?s7XjH1LC) zLMKIcEuPxEZgQF1y2&6v(mfvsO+Dj4)+6{XW+!-GZ0cD~sY%hXP&m|6g@7OSP&|do zkKs&6FU3#DF#cmmRpn-H_j*OH;^CcM3mt#lD$=G!t7dpz6n}VO)ik)dYCeJNUYx-0 zcLe-;iwy2wZGul~A|dd06Z~T}MOl@%FN8&Ff{~2HYlMo5Wn%`MT2nv~(%;tz+3~<; zBjl}3qfBY(+5{?~U>;)Gm0A*JL;M983Wt_RSo)Sj&2LApnT^k7X8_FequWU(y z{?#FHc|)|o?-WE-dAqP7Pw~auBSiGq`?N4=qwx4IsB&x~EAjILk_hCmYcu(^fz8dh z%TQ92V8NF0UgXN)p)E#cN8{j=Efz@FI+5<&v%5p-=sLIZFV8W?IJh;E)(tWkGzx#; zT0+mpJIZhy4CV2Jci)lVWfOqAOAC#AqT$*dOX_Y$rK$+oNImgYll=hFwh=Tw&22dJoAaZI35XzS_Q&?HvtuI~F0Tl?S5W z`5h5an#y_X-RRIu{pTH8cQIGs$QCG0IuiZ%( z@XI^Nf+z1Jv);C|7S8PKCAK6vWI*3t(fJ<3Na|g6w;AAp7`X55c}UF1cRR4D zxF^-jKAJJJ7#6wq_N*SF#2!L0vKB3oEH%Tx3NEmFjQ<7XNs~GeRMRrwnRf99-$o1jLPI zI`jb(T-g$0T(%4g3kI&!J!IZDMW9m+ zs|V!p#a_9%asPk{c4=dLbg(f+nWSdt!k6_%I~jIS=xX2@{4cM^k9%|AzI)T4^t}*R zurCI#*M+cK%vatpC*8U)6Q0;tK^6eVuk9-!hj*hc2#Stz0leda_Fv^t2vdI~0*?J^ zCh|4xG8p!gKFrxqKH}c}7KrGMf{XjfEM3{Z1DC8kFheG|LavlqDM*699w2S{_P`tj zD84_RZf4ugTzLF`6TEr99fvaxk|bvwq$_^t;3ORMQgbXLfIw`E&K;+C5Y7nS2O*gcpx4gde{VQkF@-s;Er2%F6W ziQhd*lV>0ET81B%NHbF(w}=SEV?F)2miGGa@m$!>zA{RBLK(n+FG8yp9*G3U6M5oP zpzy7TH0OIPUz`h;z7mmU4;(8JX9I-~MBt}m8L;4p5=_Ud=n63w;DO^N8hht>53A5y zPNcBkB`31k@0Jr&VAIhg*m^<@-<`;Uc}G;BKRKPvw4BUkzpN6;e(}AYyFUWnKZzCo z|2&z%ey^M~vEQgu*)%ilR4jYKu1y^{d?ug$t~irMoFMdpGdw46ox$BppG;=I z7#=vEL^^jpIg9D2d^7kog|c5hO;OJHXQ;XD88R@3pCOxY_?bkw*ervHvt^XrEj$|rzpssg zJ!ev3_gPh*co(^KtjI!^SB3$@I=zlHLqEQ)=(}e6!^|;b{xB2XKHDmObvs!0?B`)H z{gsD<@q>%LKDF8k5r2(?o3Gq}taG_g|9PZus|{A3n+%n&VR!KN=eozj$=4eOi{E(P zH=B~?=?=G@eHzy3cDl?B#^FPKeV+HJYuAC|jTlHg9|d>49tnvT!r&!i9Av-UKbU&q zW#7Te?_3I%S+&|8XRD3NQV;&=(oNsNo1gx{2VVX!7HEDN2U(vfAo8;hed^)HXH$Ij z2Bg>VG*YEQ2wU)w#%W<&h#~Z%Q zrLgzLBw48rt*W_8*9tzLGKa;A7(d<6`x|hSMs6LJ!L*yYU>q=X>-B}O=4OIVA>4m+ z%4i!l8rp;nlYg4!zo9|D7Ne#{Gy5dfX4WBH D=e6*3 diff --git a/src/restangular.js b/src/restangular.js index 28e73584..db940311 100644 --- a/src/restangular.js +++ b/src/restangular.js @@ -233,13 +233,17 @@ module.provider('Restangular', function() { return angular.copy(idValue); }; + config.getFieldId = function(elem) { + return config.restangularFields.id; + }; + config.setIdToElem = function(elem, id /*, route */) { - config.setFieldToElem(config.restangularFields.id, elem, id); + config.setFieldToElem(config.getFieldId(elem), elem, id); return this; }; config.getIdFromElem = function(elem) { - return config.getFieldFromElem(config.restangularFields.id, elem); + return config.getFieldFromElem(config.getFieldId(elem), elem); }; config.isValidId = function(elemId) {