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