From bb04f466527a9f3254399bae6641a55357dc511b Mon Sep 17 00:00:00 2001 From: Andrew Ivins Date: Thu, 21 Aug 2014 11:18:22 +0800 Subject: [PATCH 1/2] Keep track of baseUrl root path and handle path-absolute resources. --- dist/restangular.js | 2547 ++++++++++++++++++++------------------- dist/restangular.min.js | 4 +- dist/restangular.zip | Bin 75378 -> 67893 bytes src/restangular.js | 11 + 4 files changed, 1307 insertions(+), 1255 deletions(-) diff --git a/dist/restangular.js b/dist/restangular.js index 47e53395..076f8997 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 - 2014-08-21 * @link https://github.com/mgonto/restangular * @author Martin Gontovnikas * @license MIT License, http://www.opensource.org/licenses/MIT */(function() { @@ -8,1298 +8,1339 @@ 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; + config.baseUrlRoot = config.baseUrl; + object.setBaseUrl = function(newBaseUrl) { + config.baseUrl = /\/$/.test(newBaseUrl) ? + newBaseUrl.substring(0, newBaseUrl.length-1) : + newBaseUrl; + // remember the root path of the url for path-absolute-host-relative + // self links such as /api/v1/resource/1 + var matches = /^(https?:\/\/[^\/]+)\//i.exec(config.baseUrl); + if (matches && matches.length == 2) { + config.baseUrlRoot = config.baseUrl.substr(0, matches[1]); + } + return this; + }; + + /** + * Sets the extra fields to keep from the parents + */ + config.extraFields = config.extraFields || []; + object.setExtraFields = function(newExtraFields) { + config.extraFields = newExtraFields; + return this; + }; + + /** + * Some default $http parameter to be used in EVERY call + **/ + config.defaultHttpFields = config.defaultHttpFields || {}; + object.setDefaultHttpFields = function(values) { + config.defaultHttpFields = values; + return this; + }; + + config.withHttpValues = function(httpLocalConfig, obj) { + return _.defaults(obj, httpLocalConfig, config.defaultHttpFields); + }; + + config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds; + object.setEncodeIds = function(encode) { + config.encodeIds = encode; + }; + + config.defaultRequestParams = config.defaultRequestParams || { + get: {}, + post: {}, + put: {}, + remove: {}, + common: {} + }; + + object.setDefaultRequestParams = function(param1, param2) { + var methods = [], + params = param2 || param1; + if (!_.isUndefined(param2)) { + if (_.isArray(param1)) { + methods = param1; + } else { + methods.push(param1); + } + } else { + methods.push('common'); + } + + _.each(methods, function (method) { + config.defaultRequestParams[method] = params; + }); + return this; + }; + + object.requestParams = config.defaultRequestParams; + + config.defaultHeaders = config.defaultHeaders || {}; + object.setDefaultHeaders = function(headers) { + config.defaultHeaders = headers; + object.defaultHeaders = config.defaultHeaders; + return this; + }; + + object.defaultHeaders = config.defaultHeaders; + + /** + * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override + **/ + config.methodOverriders = config.methodOverriders || []; + object.setMethodOverriders = function(values) { + var overriders = _.extend([], values); + if (config.isOverridenMethod('delete', overriders)) { + overriders.push('remove'); + } + config.methodOverriders = overriders; + return this; + }; + + config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp; + object.setJsonp = function(active) { + config.jsonp = active; + }; + + config.isOverridenMethod = function(method, values) { + var search = values || config.methodOverriders; + return !_.isUndefined(_.find(search, function(one) { + return one.toLowerCase() === method.toLowerCase(); + })); + }; + + /** + * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams + **/ + config.urlCreator = config.urlCreator || 'path'; + object.setUrlCreator = function(name) { + if (!_.has(config.urlCreatorFactory, name)) { + throw new Error('URL Path selected isn\'t valid'); + } + + config.urlCreator = name; + return this; + }; + + /** + * You can set the restangular fields here. The 3 required fields for Restangular are: + * + * id: Id of the element + * route: name of the route of this element + * parentResource: the reference to the parent resource + * + * All of this fields except for id, are handled (and created) by Restangular. By default, + * the field values will be id, route and parentResource respectively + */ + config.restangularFields = config.restangularFields || { + id: 'id', + route: 'route', + parentResource: 'parentResource', + restangularCollection: 'restangularCollection', + cannonicalId: '__cannonicalId', + etag: 'restangularEtag', + selfLink: 'href', + get: 'get', + getList: 'getList', + put: 'put', + post: 'post', + remove: 'remove', + head: 'head', + trace: 'trace', + options: 'options', + patch: 'patch', + getRestangularUrl: 'getRestangularUrl', + getRequestedUrl: 'getRequestedUrl', + putElement: 'putElement', + addRestangularMethod: 'addRestangularMethod', + getParentList: 'getParentList', + clone: 'clone', + ids: 'ids', + httpConfig: '_$httpConfig', + reqParams: 'reqParams', + one: 'one', + all: 'all', + several: 'several', + oneUrl: 'oneUrl', + allUrl: 'allUrl', + customPUT: 'customPUT', + customPOST: 'customPOST', + customDELETE: 'customDELETE', + customGET: 'customGET', + customGETLIST: 'customGETLIST', + customOperation: 'customOperation', + doPUT: 'doPUT', + doPOST: 'doPOST', + doDELETE: 'doDELETE', + doGET: 'doGET', + doGETLIST: 'doGETLIST', + fromServer: 'fromServer', + withConfig: 'withConfig', + withHttpConfig: 'withHttpConfig', + singleOne: 'singleOne', + plain: 'plain', + save: 'save', + restangularized: 'restangularized' + }; + object.setRestangularFields = function(resFields) { + config.restangularFields = + _.extend(config.restangularFields, resFields); + return this; + }; + + config.isRestangularized = function(obj) { + return !!obj[config.restangularFields.restangularized]; + }; + + config.setFieldToElem = function(field, elem, value) { + var properties = field.split('.'); + var idValue = elem; + _.each(_.initial(properties), function(prop) { + idValue[prop] = {}; + idValue = idValue[prop]; + }); + idValue[_.last(properties)] = value; + return this; + }; + + config.getFieldFromElem = function(field, elem) { + var properties = field.split('.'); + var idValue = elem; + _.each(properties, function(prop) { + if (idValue) { + idValue = idValue[prop]; + } + }); + return angular.copy(idValue); + }; + + config.setIdToElem = function(elem, id /*, route */) { + config.setFieldToElem(config.restangularFields.id, elem, id); + return this; + }; + + config.getIdFromElem = function(elem) { + return config.getFieldFromElem(config.restangularFields.id, elem); + }; + + config.isValidId = function(elemId) { + return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId); + }; + + config.setUrlToElem = function(elem, url /*, route */) { + config.setFieldToElem(config.restangularFields.selfLink, elem, url); + return this; + }; + + config.getUrlFromElem = function(elem) { + return config.getFieldFromElem(config.restangularFields.selfLink, elem); + }; + + config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId; + object.setUseCannonicalId = function(value) { + config.useCannonicalId = value; + return this; + }; + + config.getCannonicalIdFromElem = function(elem) { + var cannonicalId = elem[config.restangularFields.cannonicalId]; + var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem); + return actualId; + }; + + /** + * Sets the Response parser. This is used in case your response isn't directly the data. + * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}} + * you can extract this data which is the one that needs wrapping + * + * The ResponseExtractor is a function that receives the response and the method executed. + */ + + config.responseInterceptors = config.responseInterceptors || []; + + config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */) { + return data; + }; + + config.responseExtractor = function(data, operation, what, url, response, deferred) { + var interceptors = angular.copy(config.responseInterceptors); + interceptors.push(config.defaultResponseInterceptor); + var theData = data; + _.each(interceptors, function(interceptor) { + theData = interceptor(theData, operation, + what, url, response, deferred); + }); + return theData; + }; + + object.addResponseInterceptor = function(extractor) { + config.responseInterceptors.push(extractor); + return this; + }; + + config.errorInterceptors = config.errorInterceptors || []; + object.addErrorInterceptor = function(interceptor) { + config.errorInterceptors.push(interceptor); + return this; + }; + + object.setResponseInterceptor = object.addResponseInterceptor; + object.setResponseExtractor = object.addResponseInterceptor; + object.setErrorInterceptor = object.addErrorInterceptor; + + /** + * Response interceptor is called just before resolving promises. + */ + + + /** + * Request interceptor is called before sending an object to the server. + */ + config.requestInterceptors = config.requestInterceptors || []; + + config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) { + return { + element: element, + headers: headers, + params: params, + httpConfig: httpConfig + }; + }; + + config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) { + var interceptors = angular.copy(config.requestInterceptors); + var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig); + return _.reduce(interceptors, function(request, interceptor) { + return _.extend(request, interceptor(request.element, operation, + path, url, request.headers, request.params, request.httpConfig)); + }, defaultRequest); + }; + + object.addRequestInterceptor = function(interceptor) { + config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) { + return { + headers: headers, + params: params, + element: interceptor(elem, operation, path, url), + httpConfig: httpConfig + }; + }); + return this; + }; + + object.setRequestInterceptor = object.addRequestInterceptor; + + object.addFullRequestInterceptor = function(interceptor) { + config.requestInterceptors.push(interceptor); + return this; + }; + + object.setFullRequestInterceptor = object.addFullRequestInterceptor; + + config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) { + return elem; + }; + object.setOnBeforeElemRestangularized = function(post) { + config.onBeforeElemRestangularized = post; + return this; + }; + + object.setRestangularizePromiseInterceptor = function(interceptor) { + config.restangularizePromiseInterceptor = interceptor; + return this; + }; + + /** + * This method is called after an element has been "Restangularized". + * + * It receives the element, a boolean indicating if it's an element or a collection + * and the name of the model + * + */ + config.onElemRestangularized = config.onElemRestangularized || function(elem) { + return elem; + }; + object.setOnElemRestangularized = function(post) { + config.onElemRestangularized = post; + return this; + }; + + config.shouldSaveParent = config.shouldSaveParent || function() { + return true; + }; + object.setParentless = function(values) { + if (_.isArray(values)) { + config.shouldSaveParent = function(route) { + return !_.contains(values, route); + }; + } else if (_.isBoolean(values)) { + config.shouldSaveParent = function() { + return !values; + }; + } + return this; + }; + + /** + * This lets you set a suffix to every request. + * + * For example, if your api requires that for JSon requests you do /users/123.json, you can set that + * in here. + * + * + * By default, the suffix is null + */ + config.suffix = _.isUndefined(config.suffix) ? null : config.suffix; + object.setRequestSuffix = function(newSuffix) { + config.suffix = newSuffix; + return this; + }; + + /** + * Add element transformers for certain routes. + */ + config.transformers = config.transformers || {}; + object.addElementTransformer = function(type, secondArg, thirdArg) { + var isCollection = null; + var transformer = null; + if (arguments.length === 2) { + transformer = secondArg; + } else { + transformer = thirdArg; + isCollection = secondArg; + } + + var typeTransformers = config.transformers[type]; + if (!typeTransformers) { + typeTransformers = config.transformers[type] = []; + } + + typeTransformers.push(function(coll, elem) { + if (_.isNull(isCollection) || (coll === isCollection)) { + return transformer(elem); + } + return elem; + }); + + return object; + }; + + object.extendCollection = function(route, fn) { + return object.addElementTransformer(route, true, fn); + }; + + object.extendModel = function(route, fn) { + return object.addElementTransformer(route, false, fn); + }; + + config.transformElem = function(elem, isCollection, route, Restangular, force) { + if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) { + return elem; + } + var typeTransformers = config.transformers[route]; + var changedElem = elem; + if (typeTransformers) { + _.each(typeTransformers, function(transformer) { + changedElem = transformer(isCollection, changedElem); + }); + } + return config.onElemRestangularized(changedElem, isCollection, route, Restangular); + }; + + config.transformLocalElements = _.isUndefined(config.transformLocalElements) ? + false : + config.transformLocalElements; + + object.setTransformOnlyServerElements = function(active) { + config.transformLocalElements = !active; + }; + + config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse; + object.setFullResponse = function(full) { + config.fullResponse = full; + return this; + }; + + + //Internal values and functions + config.urlCreatorFactory = {}; + + /** + * Base URL Creator. Base prototype for everything related to it + **/ + + var BaseCreator = function() { + }; + + BaseCreator.prototype.setConfig = function(config) { + this.config = config; + return this; + }; + + BaseCreator.prototype.parentsArray = function(current) { + var parents = []; + while(current) { + parents.push(current); + current = current[this.config.restangularFields.parentResource]; + } + return parents.reverse(); + }; + + function RestangularResource(config, $http, url, configurer) { + var resource = {}; + _.each(_.keys(configurer), function(key) { + var value = configurer[key]; + + // Add default parameters + value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]); + // We don't want the ? if no params are there + if (_.isEmpty(value.params)) { + delete value.params; + } + + if (config.isSafe(value.method)) { + + resource[key] = function() { + return $http(_.extend(value, { + url: url + })); + }; + + } else { + + resource[key] = function(data) { + return $http(_.extend(value, { + url: url, + data: data + })); + }; + + } + }); + + return resource; + } + + BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag,operation) { + + var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common); + var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders); + + if (etag) { + if (!config.isSafe(operation)) { + headers['If-Match'] = etag; + } else { + headers['If-None-Match'] = etag; + } + } + + var url = this.base(current); + + if (what) { + var add = ''; + if (!/\/$/.test(url)) { + add += '/'; + } + add += what; + url += add; + } + + if (this.config.suffix && + url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 && + !this.config.getUrlFromElem(current)) { + url += this.config.suffix; + } + + current[this.config.restangularFields.httpConfig] = undefined; + + return RestangularResource(this.config, $http, url, { + getList: this.config.withHttpValues(localHttpConfig, + {method: 'GET', + params: params, + headers: headers}), + + get: this.config.withHttpValues(localHttpConfig, + {method: 'GET', + params: params, + headers: headers}), + + jsonp: this.config.withHttpValues(localHttpConfig, + {method: 'jsonp', + params: params, + headers: headers}), + + put: this.config.withHttpValues(localHttpConfig, + {method: 'PUT', + params: params, + headers: headers}), + + post: this.config.withHttpValues(localHttpConfig, + {method: 'POST', + params: params, + headers: headers}), + + remove: this.config.withHttpValues(localHttpConfig, + {method: 'DELETE', + params: params, + headers: headers}), + + head: this.config.withHttpValues(localHttpConfig, + {method: 'HEAD', + params: params, + headers: headers}), + + trace: this.config.withHttpValues(localHttpConfig, + {method: 'TRACE', + params: params, + headers: headers}), + + options: this.config.withHttpValues(localHttpConfig, + {method: 'OPTIONS', + params: params, + headers: headers}), + + patch: this.config.withHttpValues(localHttpConfig, + {method: 'PATCH', + params: params, + headers: headers}) + }); + }; + + /** + * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API. + * This means that if you have an Account that then has a set of Buildings, a URL to a building + * would be /accounts/123/buildings/456 + **/ + var Path = function() { + }; + + Path.prototype = new BaseCreator(); + + Path.prototype.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 if (/^\//.test(elemSelfLink)) { + // self link is not an absolute URL, but includes a leading path + // the the actual resource. Prepend the root of the base URL + return __this.config.baseUrlRoot + 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. 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..2fd88ef3 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 - 2014-08-21 * @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;f{*p9R=Dp9nzg|C|^!|S5o_p@O z=bm%!Ip_ZV_^XH~_C&<@bcRRBFJgbj1*tQ3$LA%n)*W_b;qoIFUBrIMahXPzwtN2E zh}zPMa+H6tA00B6&SVnW+%CIgVAx~vIovL}AdFZ%L73RFv3(^ zEJt;ieP*$Va3yT7#V&SJgo^3nQYRJdyH>PP^cAxK?@v@33`Huy_U2m}SUe8Ywq< zM6Y|;V--#1etfobt1Le2py;L5m@d-|`g}v)ruwC(rKTm9Ej9I(=$D#I4x_kMwCW}= zRxgP~5FBr9O|gFstbzE=pF9&`aHTaLtyZd^1dMX zQ8|X<(_`621~+ZXlm@!(j424Yh!cpXsjCV#cFV4V?0vgQc)h4 zh3{IFaK)*@Xyr-IrQ_K+Rrn|=`ZsYExG5$cI~8$gi_eT0B@s=qzAwHGAIF<%fmJvY zpMf1YS|LtpUKVWP*&qAr5Ud%>rzMc{;`vGtu|$aH1Y1)sdzOdBU;Op zTuZC?Q%VI&Q>)RwIZ>v?XP>90E!FzStvDUFP%h+qR;S~Q)P$ezadz7BF!C}x((-XQ ztqf<=8riEkUBrXwom^Fp<*A^_D4OZ8)eBl88`wRMn{0_dybVH!EFRJ2>-C!pbx#Hm z9*$<@60gTIQpbX$tQMEc?Q&Qx&RJ}hWy?YVK`hZ{8DJBNUZ2G^FzmE=W)LLU&Fps$ znJ=LXdPI8=&b3bRlzzn{$Q*KsV}$8{3?dH=lS*po{{)0EE&}sX-Xm729j0x5TrzA#6s)2=*b0v(Qz$Rm_CL{HU zwh-J22*WI5Zb6{6&Tx_!b@>T8s3+!1v~;@H+kek|RLK zEkt*&3M081+?!jhv$=Z~%qI%G4N|4IxflDv^uUOusq)dPN<+U&g=TMFK+c&h7EqMKqq;h=8b7cEKEtF1KmJ}C)u-5Y|^tM zr>t=H1gG~ER^Uvb-?=vymBn~O->}CeXtfwCYL-a7Rb;pznJOfF6@wm)a)R70O(l`5 z^>;)nLOdP}S_f{`PDPnEa|%CYh~3~JQ>JT#PuGqYbS8xBE7X2AELPtza~u=$V{+#) z%80%npXo0_Q*k?UYvaYX2s+Q`Kr5Mzd%6?xS3?puOpR6AEItbtf_q4mf-?h-ma3)% z#PLaq|D204)KW(tKbAKeX5(!`BeF}g&{SH5jipnu(4fMn&qPa`^!L)N;086CRBR?J zFe;%e$;M}2N5zV3nNRS!J#15gt5noxhf_>}_}UC~mc%I8V%+^JM61tevDs#LJZ=wG zPcVXHUrWq_G31jmaoCmEc5;8gpvo1sr{r%O_IoGDI_l!~%@2P%QM~{54AK zGL^+K$#9kBqWg`hm%INvrmEm5E5@mcOuhhRg%Uqh=&-aRCZGpQU&`}w|0RiVmdkNz zRR-=X&y6SDWK#rG^wtL?34{>9R)ZclR%GFs3J+>4JFu;?6Q5MhMA_5~Nv)PnZNWWL zoye?GqVr@9o?euNbybb{AUhTZ%9U~cIsA$plwoOgJ1t)zhSfA&z^|)QCgfC9(~Ui~ z1=v;Fgm-I`!ifg{QQL<0I&M?a#2fN5pJ0=UztA{Rv%yXL>yjO|O^sDQrd+y_8_Zj6?}=-Havp(+oQ@rz;uF?d?r? zyIqCv+Xd)58nC)!S}dD`X(pH4haJ^8JVQOn3oV=3o8ZT=Tby2zk`VOGEC^>=)0vqw zV4W$Tdsdt@p=;K1yfdo|o>^&8_F*R_JHSv)O=p;Ipn zmd(k=mQD>0b>?C)KRE!Lj=6I(uzF4=_Rh(}@i`rADXOkQsefVDEZo|KuUdV6s3 z#VPP!T!IHLF64pwgBQmla(=ENI0fN$%`d?f^HZ>Yej1L=&+j3JCzO~4t%+h?X~)=} z1)lu9YX&X8iEfti(Gcnjz$gWfMmX%)Ig}c&(F7?FGG6~K2ZtA!VucVm68@fpjD=~a zU8v^TI<&AI#}`gV(j`sExHOCVq0W9huxh2gOSbd(c?@0UmKu_cmV zl2K-(^wpMy^)?;)cBIpDEgqo+E%qS=cC3@5M>H^k;Y1KOi#>SeKrEk_*|XW69i!Fi zX(HBZu`99Bo*Ts-jvSBMr{O!hj+s%OzYOCyA;J$e74j$>2ePD1ecYaLL!K-aCkG1f z{eTfwgDu!JI1MKT8xZHHLyw~sx6tnkM-$3d6yUNI=@?s~#)B(L@aYOJ66T=_R_1eS zS-P?b_pB_$TPrJ(=&WH~ea>v&wbMBb?>Z~-=qhq(s|4Qm@>YSZt5oz}Og{y_SyieH zFF4MyEs9B`{i{INLe)))lQWp&#q6>-cP7Co2i zMgloI)4qT;Nl$W&4!I=}@1C6l)#~^t>R`u6)9L~Yt~TM;)phu2bq%sTnP~LP#&w<| z+_Nton{FwSg3MtJjMqn@!JCY2!>aMoa9J|8dh7Yiv*EHteCKUrFR=OM@b*1l3-5eP zCX=Iccq+Xt#FxVh^T{ygTL!qHQnJU&TR%!zo|P z``gMj6pql`*RC=0_RJa+YsXJU$`oi@n}vb3^(ZEFKD@S+w|`$-hU&|!vG#Hsx~blH z_VToFW+B45g=i2{VOk%BTh~#9S&)pA>oVp3w1iOhEJ*WXWKpjimh}Z8z1TcA9j|&- zcnt~oe0?@IQ2)D<7Q&Pva2~KD1WD5+eSBa;1HRc%m&9Edi>(P+yU0Df6fL6_=^@9C zAc(x19Y?@uNeU~pP{G0}7%3gql~?FlJ*IJU7QP)#WE!8nF%Q!>W`{DFsHw%>8w;^* zQ!3^@7YXAPbfU8`p>WJVKr-5~EN&%_juj^Ge1tqnAO~^Xk%{leVv)3|7+sraTQ|+- zxWA?6!p#{{32599Bdd#_DE;uYGEOd8pMtZSvwu3Z{fd8YxDL-;nUfMYgh{9TA+lGF z+xO+-({0hvZ!yBMr3(AERN~_;J*c@#pmquduPRbfX~jGEBmNFuWl}KP^_NL;YFnyo z7A`Bzpro0H+$|v)jBZE@$%8Q*TOzYO_U8{7{V0JAXFekB>QWrJs{re+&d0r1t5CSD z5IUuq}7mc{lyp( zZrS!S3~pBg+p}udCE7Gl~}Lpl*p&CZpO2FOBhX6uWS+ zQ7GUOwRrqGRay|$=4M^iVXx1Flu1Y2ti-+%s!)N;xxaE2^UcP=V`fusM&|7&+mmF`5N;wT^U9Ky5jCGe0aARh4*NvE1xW?5I?7O^PvKqm>1`_s%*qPA(7v7?{fV9 zUON`wmlLC%K?fR-u(sW&!O{DsGQay#3Dwq_Q=NK$3!j`#eCqxJ{BVCI8XoAxt_Nn} z^9NRiFk{B6M-`d;N{3&qu`?i07-q*`#6z0oRl%MWUbkzAIs17Fl=$pu8M+?UV$;Kw z#FhfDJUk!8kJxbYBiS0d5o3V}w{uE`B%_x8^MVsy;PpudTiSs_tV(4i{7VjWMz9sJ zkUGb$eM+EX(HyEes>x{``Ms%6G80jk2sNFtkvz`$c|JW4z1_S1;RAxDyR28PM?MJlrR5ZOiGjS4>xuTZ-ynl=5$rOlXj2%={44@_n#3lWN zG3w2S>G~+L&>#K%Upd~hfX)edEDpX{g*zXr+4E*3)<2Pnokt=O^>7aAUy4Q9!6NKC zmWNFT+i7hD`1YWX5|RoD~%UQJM7#gldZVSc9CW zS~2)k9l8Gky!X^*SdX+S$lUlDLI)OvCsicAD7y~Pj6P>re}Kb*Jm=3^K7!hPmHcq)g<+q`1JV#JofAoeyTkGumUa5DgU+N zkSYCZ#qpQ*LZr+UXOh@iCxx46nWxNU^`HwI0COvk+-E@9^K!&JU&=h;spreF_IWj> zfpI)rWXckDtdMUE)3K$;DoK$#xDq8DSO33NqFO8NR(Y;y3$%c#E? zqD{rVDno{?d=mF8YGdKm4+d zp$9bC@`{F2x#^V}3Cyqcg4oTk)-v!E|HWPq{^hIn3?37_*9-RS{na#Sgmkk<@Kj{J zmc}P(eys;Ly*7_O|KqjGG5UHA?tER3x;HZL&FfOdtnQ7OeBrmf(TcC$D8`1q94sxT z3$K%@*n5(0GWw$N%*kwm#o%iymZzP{p-3qn3r}6bofHXr&zl-L7ZLq#WnbA8laqgXR=-(|z|GRa#>)jgsC;im#&AhgB#dSnt7)zetX(CJHPu4@nWd z<_o|!Y*^A~uuNQ`9XO}P&(0^}%(+zj`RhKxV)Tl>A77W5Ck7mx@y&0;aM$^VvN&e% zBt}Uk`8Cmja|L*7n-V+DXB_PPkTXi&Bb)Q*XHg1WOWgY&n2-yp=FoSLUxhPp7YtKZ; zGAQ~ge;NIY%!AT-OdqF zXB)peR@Rlb-|qPQxp9)B!{cziuwrU;7;D9iYQ zblGy*>hbq8Web`EfzJgt%Mc8L`t|Gm((2b)41EHO-99b={`yom;I9{47>kVrd9JeINQrXEnepjwc zS2AcA){pYiv|G2rU^RGk4#R*!G^ooAtZZbk>j|NOBaENRm9^*mPmDwTu`zR$&%mM$1t;VL{?QIOzv+6 z46Kh>vkM5#RX6b=Nh%vA4KWmNR`uF3fuT zD9rP*!GfWHoXkwF+-_^{>gab^U0rsYk`w+0tPW{}cW@SN<500M2!HpPsS)8a)6=Cn zP~h7x4Wwr(CkFz34Kg|dj`>EI^&PxRZ{qZwytR$C_CC6@lRml})>fWA5Rl$3r%T^l z3>SF~eC3-0F(V-m@0TYnKHijD^~2eX3Jl&V?5#Z;c5k~{^=BLlhc zRItCqrH4U08{965Pt@F!kT6&flEPNAhZIrl_lC@b@!jF@E@=ZpqeAo!evN&A*9&aG z)&>oH%Lf6S+Zd|xqS@O*OBu#zLU9MekD(fAX;HliVYzg*+OS!)*&bF7PllDj%jJ;? zL}YH;m@?_Xheq8kArtxN3=e{XYHg^#&+6*bi}EITln^zW-@L3)!Cmi2@}&+m19 zL|{-m77gDMCHT==}(h5U%X+*_p1%zTdX#l-PYA=?X9BZtXkFT*5L&a@3MAK%0zrtM+NEe0_fy> z+iSXPJp{PZ!MA&XAilnc+rpy4RU#mRH6##_*}{_-1bzKArXqa3z6z7dWB#{Af&K%6$~-l)=mO}trw~Yr$nbrPgVyB zBLr6J+d`Cwo!6SUmbhXU-{v-Lvd7CDBDRWNehslBN|E&91=8BqM#zGbk)gz7tRHHE zJYe9JNNoruvmT9-Dh&-Qx7Ti?0&AtM#|&oWU2RS>N`5`YATdhF7|NoOnS)ss6~!Lb zMnxIK9l~)=et>saiHOpKr!;pR!r`cdm`Px3Z!gLvjXk7M%ZEQl#gmw!f=f|}*s7zG zu+58R{SDD6_`V_9$Ot$b9hW+Zv&~*#x2S=v&mKINe?D5D><&7wfxY*)vmu~oOn2oT3&;vy$;I;{glylJ#aQ^K2rv2Z1(2xBPmG(HxZUBHT1 z^uJffz!Mm}XmWZc$(zDXVm@AKjpe_~U#2GL_+8=ME4tGl& zN1Fq27I-Nx89t3mlY-J@JR|_x_-ro$q&*&k@!t4MH(Y;?p8-F|>o68yic6$%NDQV@ zge4ej5)4v!U<4+@?gUMMk=sZ%9el+{5PDN!Q=%L$CIop|JU%fEbCT-BQaExpz+dE3 z0|ygR!Y767;9dO=8>iR9vx!r)mq-X+qktq%8)V|pQG2YO3R}2XFDpOJ8HAOyVKWyD z`_Bfst^S%$4fVPjrub1^fs~KFrc0z^x>}P<7Dd8zeGDBe)7Q}ER(&oi|2O(R0H=Iv8c#@dwW-XJMwcc;u}l6fa(4tpT`kv-(4 z7Gv;Vo0>z6M$Q8zgfo_o0g2UknqXquOhr+oGHOMm(sZe}4$WG6ebii?fZa1RB zo#`!}X@> zn#=_V`a@bM>`jY_BaV`5@3*zm%GE$NygQ@tq(KL&428rcofMKC;bqr0WhKBUG_ZU|j0ZitW)vblFV0B5iUTdAw}pUiX11hpNrx^+KToP_8j!kYW-)=^ zIWvvejS7Aul{KCC>dZv6od=0G_TbT(b?s;aw-y$my%iTF!)Jvd-o5da!dw9;M~X2l;xhrY?3TfBk#Ko*yKTjS$c`4LB1g{@&q9tb7tf*ZD)(AmKu|p^4;}2b zS!rH~h=7-7Wy`sCYp;{%ir~zw1dS)xOcD_GdLlGi;&4yJS|VVdB^1vT=yB6`mO%KO zMR246vvV+yqn)+0Lj)^|VDhE@fc}Zn2FGjzqC7S`UJ{{)lX!aee60vgYH7ErVI@Xs zio2hI*(GJ@Kn6>a;r^yT=s6H3CPh==V&TOSAv?ZUlE6ePFHJRdS|0pFL61(A0-oZqd2kvT~?cAA_Fo|}_~$;c^^ENm{%phiAY&SBRpI#h3Ac*c#o;Pm&1WY z#$%On^xZc#72d9_qmQ!GR89xKtBZxj^H6}$Gf$7Od*)TDBxZ@0|2l6jQWbrDDK5JB z`gm-k=Eq~Aq8$$*35+q8D^wJG3B6+Q8JAh^S$F0SEO% z@<4Zt@RtSCnYoQOhC^5#9=CpaFp2K;in@hp1N-V&SU*))Dg~QQ>lh!(MOuih&%m8V zQ-$m6ncn0hF=*ZEZ%9L#>}Uu=T%y%KenSQJ|8&DboLjZ965E{%6X-m#hUdxg2RRj> z_N~vy2%;93{9<7uq6%2VE*5q#N`af4osBlP)#pu+_N5LXUOvT-I~XVj*pMb8!jl314zOduS9N_AjYmJ%3$7Q$;0Q zSrUnD#8S2=@|K!VbgPy!`(Vk6Ocd>$Wd-;yI6b&& zSvbNUT*k_s*9qMH#QSTTIBZL>)k1eu0(sX0G$oRD4xM9UQlkD@M-FAJYn(>0Nb7gM z79zR|TOP;QN?VS#YZje^H!7UP!rA(Jdug#Db@bcX`1URv-zFkt{`lDP_%H#6TaFLS zLd^Qx@=WATxq_Kz=89x|7gM>FE7GWmCyIKdEpKbJw~-61t{eJ!$9ltn5w}6O-m`dS zGd%lXe1$zoL`BVjodaYxCx%N-V$wRGp*e{$-`|`azyRQQb0PH%Z#5UwCUhmH0>+i~ zaOa6|xM?Mxaon&m9y+#V`C{A)hl17wIQ@12XjZ9FgY^B)57e-DRgvILWC-)nszn~} zi7hoA@3*y-dAzIMSHh*190}+5ABMmptI-4Ipp{w8tkoGc)ZJ7 zv)tZ2X&7tGl-7IsfC_$SWg%MAR)l)17TRrXEV4Y&R)|)Av8@Z+@AyD+(Z?dMd|xQe zxsk8K_R-zJ#04qg13nsI|IN?v6+E2iXGDmbbG`$kdk`^o2gf@cc6#d!s!#H% zbt;7(;-jJF6N?Y?%1e8~;J(gKkA*VsQNbTO1F`QiAFibS(L`K2vtYPPZh(Wj=Hu|m zQ7zauhQUx*loZS;ZKCA4;$GZSh`6Z z=$=9IAlTJC6ON6>3p3MkR!Gkb#Ms{(g$kh@)cup4(u!`!lLIA@eu_lJe@kvzFSFGD zD{_&=Tji+#KPCgN+ci7i-IeF(KpLGA#ua(1@HMCM!!avsJVjFIoiEX8=9@~93 zmP=3AGUeoU!P&_IYPgtK!etw#?QqN2jnbONb$HF zao5BXROzYD%qbg{25%U_6 z0Xfc?&g{tD3Rw@)LIt~~ak?4k9#3Q z7Opb$*49qLa&{$0Zk6!z4ezk%w;gR{OS*V{vuW}K&ECg5gc^pXMiPQHJDt$l?{wMM z=;7Z3I&?{)>uRuVTvvqcj&-Tno>*6p{>MBR0|A3^*e51q=D}tdYf{6m!DR^Z7J;FM z&|;eP(_j^q9u|AdSR|vNEN)z%=jN~z;KKScG$QQ==1Nv=pr;1RQ#`t%mfROL;`pMC zJoeK)9k9&=Uv6ZsvT9pB8YC@8do~H_>`*G4+Qceev0PpMNHAR9#4Ppony?W7BD`uS30gjnfZq?XK&T=(hi(oAAm{KeCheV8<`KFbKd^ie`G6jUnyJJ zPOsToEr=#oa;jHu34_}=s^Fuotn6gsDo2!6+m=eGDQj_Hgepb;0drh)Lv3Qy=u@BF+e@za zX+;AUc1D1KkSJEgQ_z9#7)MD9nSv0uj%2C4;b9_D`V7JQ`&daYzgG{&{j6-Kr9UNh zWIq~i8fA`D&*Zd~R!riTcbGX=*<3vNBBsw}cNk#raCk7MBPwD?v*KA<`zSjr6BfPN z{=Xfa0ecA@XMR$_!UK_Tk{qCs6HwfrTMtZ!;|I#&w|iwIoXFsl19PPh#)b!j;lfEx z0E>Ij@?aLNn_LtD}C`|qAJO^B!P z)ZGl`ySwM3PD>86h%D~F^@o>Z|DO)8z`2=6=y5DL;ybbk+ha$vaD1wr8}4br{+sW? z2&%{6PbEx)G=3zC_RhHD@UTyJOb4Zxw6JP_=oGVBj_&~@aWv@C;7b1B$kAB%2RWY? zEZ4^bn?`bB@zF5c3zvpd;H6`j^@C_o^PPdP{$vPjJ{o`s8^+W~qiA@{G?)t%^yx>c zOmt_jkFn}re(!voE1FUNy?AQy|7=F^(O?K!CIE(i#R{Ovbx{E*HRf?%{T0)i>b`lX zg%ih_HyF5&ok@@0w;ub$jx$F-aG!b_daN{0k3|!Q9mkocI)0p0_6JV1Vs2wRu@a>t zo`X&HXF}@z%mavLBVx_w4)fjpMM#6_9Z!yCLhs2ql>U;j5ZHB+)fs}J`%|Z@M?0ry z%kjS!A_3b5a`Br&31H&aQ>N1|x1`bXQ!GAA*+mbWO2v)$`l%eaX;8~H82c!TZZPWb zx*y0Dho%YNXCYQP_VWYz;+R7EGD{lW_h7L&I!*X0i;QU@<}obgaC?*3hafplRw zJs1rW!q|m}2FO9Z<#aOnU3NMf1;69;Z15f7P#HT;$H0L>4fvmlgZi-uNI8RvCiI?R zhgI7793FP7$JHLU_0OGQf%U^P;b^v(&KU8&Lp^TBHh&xoA9{$+GNeyd}QH+!7!k^jMn?*GPkuWl(oi64&Z^+hL^=xT`H4(2Qv7ft`K38f&%*}tH2iQ4`MvOP8TpkzQUc~j zm2_7p)8|U) z+sEfDv`Ksno5hb+)3-y9Eo4m`T)C4}|L04k&q0JUOiD*rE4{9!7oyq=NI?oo4(6@ruw?;o~ecx&iCS{_7k%n|JDzxpNxjTJ+FZF zC$lG}J#|WPcgqM?SZjSpS^bAF^KchiMm@C-z);(>H%=B(tnG zPwm>YY4Rh4jYB4@*~z=$iI?&e7OROya+2nK`Dy~(^6DKE>;Lq7--+3OjZ{qB^!9Up zN^5HBfW51Y%hF9Od;hZU#El<4;{)?PPKK=?M$wo(ap%X$J`<0Ba<}iq(a-+VM`N&> z9ppRH-T~gwW;C&>uT%{XkGS1%TKra_*zTg=#L2y^z*mA9VHNWr9e?)py`ywX@IrgL}0?> zypk?6kxnW?5;2tUntvJ$#Zzdw6 zRx@#n^fSoGA)y0Dhv+*mc)M b;{SSxKPTUSm(NG Date: Thu, 21 Aug 2014 12:12:04 +0800 Subject: [PATCH 2/2] Include output of grunt build for pull request. --- dist/restangular.js | 6 +++--- dist/restangular.min.js | 2 +- dist/restangular.zip | Bin 67893 -> 67971 bytes 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/restangular.js b/dist/restangular.js index 076f8997..75f3503d 100644 --- a/dist/restangular.js +++ b/dist/restangular.js @@ -36,7 +36,7 @@ module.provider('Restangular', function() { * This is the BaseURL to be used with Restangular */ config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl; - config.baseUrlRoot = config.baseUrl; + config.baseUrlRoot = _.isUndefined(config.baseUrlRoot) ? config.baseUrl : config.baseUrlRoot; object.setBaseUrl = function(newBaseUrl) { config.baseUrl = /\/$/.test(newBaseUrl) ? newBaseUrl.substring(0, newBaseUrl.length-1) : @@ -44,8 +44,8 @@ module.provider('Restangular', function() { // remember the root path of the url for path-absolute-host-relative // self links such as /api/v1/resource/1 var matches = /^(https?:\/\/[^\/]+)\//i.exec(config.baseUrl); - if (matches && matches.length == 2) { - config.baseUrlRoot = config.baseUrl.substr(0, matches[1]); + if (matches) { + config.baseUrlRoot = config.baseUrl.substr(0, matches[1].length); } return this; }; diff --git a/dist/restangular.min.js b/dist/restangular.min.js index 2fd88ef3..42c912cf 100644 --- a/dist/restangular.min.js +++ b/dist/restangular.min.js @@ -3,4 +3,4 @@ * @version v1.4.0 - 2014-08-21 * @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,b.baseUrlRoot=b.baseUrl,a.setBaseUrl=function(a){b.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a;var c=/^(https?:\/\/[^\/]+)\//i.exec(b.baseUrl);return c&&2==c.length&&(b.baseUrlRoot=b.baseUrl.substr(0,c[1])),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",restangularized:"restangularized"},a.setRestangularFields=function(a){return b.restangularFields=_.extend(b.restangularFields,a),this},b.isRestangularized=function(a){return!!a[b.restangularFields.restangularized]},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},b.errorInterceptors=b.errorInterceptors||[],a.addErrorInterceptor=function(a){return b.errorInterceptors.push(a),this},a.setResponseInterceptor=a.addResponseInterceptor,a.setResponseExtractor=a.addResponseInterceptor,a.setErrorInterceptor=a.addErrorInterceptor,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.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){return b.onBeforeElemRestangularized=a,this},a.setRestangularizePromiseInterceptor=function(a){return b.restangularizePromiseInterceptor=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;if(/^\//.test(f))return c.config.baseUrlRoot+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;fnOEeYi6_WGw(lXQaprQ&^XaXQ*n=i6fGfo!d6`owiZpf~elbV-ak}=uAMq=^? z6WPso*dJ{KTlLxG>aj(B3=E4lpWa!-4;sliO# z%(_3Dy6)~oPg$EjLB28Pl59ValVoT#-1d0FvsJ=P^