├── .gitignore ├── bower.json ├── package.json ├── README.md └── angular-cloudinary.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-cloudinary", 3 | "main": "angular-cloudinary.js", 4 | "version": "1.2.4", 5 | "authors": [ 6 | "Nicola Peduzzi " 7 | ], 8 | "description": "AngularJS only module to handle Cloudinary image upload and display", 9 | "keywords": [ 10 | "cloudinary", 11 | "angularjs" 12 | ], 13 | "license": "MIT", 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ], 21 | "dependencies": { 22 | "angular": "~1", 23 | "ng-file-upload": "~4.2.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-cloudinary", 3 | "version": "1.3.1", 4 | "description": "Cloudinary AngularJS directives to show images and videos", 5 | "main": "angular-cloudinary.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/thenikso/angular-cloudinary.git" 12 | }, 13 | "keywords": [ 14 | "cloudinary" 15 | ], 16 | "author": "Nicola Peduzzi (http://nikso.net)", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/thenikso/angular-cloudinary/issues" 20 | }, 21 | "homepage": "https://github.com/thenikso/angular-cloudinary#readme", 22 | "dependencies": { 23 | "ng-file-upload": "^9.0.9" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AngularJS Cloudinary 2 | 3 | A module heavily inspired (and copied) by [Cloudinary JS](https://github.com/cloudinary/cloudinary_js) 4 | and [Cloudinary Angular](https://github.com/cloudinary/cloudinary_angular) but 5 | **without any non-angular dependency**. 6 | 7 | ## Usage 8 | 9 | This package is available via [Bower](http://bower.io): 10 | 11 | ``` 12 | $ bower install --save angular-cloudinary 13 | ``` 14 | 15 | Include the following in your HTML: 16 | 17 | ```html 18 | 19 | 20 | 21 | ``` 22 | 23 | Note that you might need to follow [ng-file-upload](https://github.com/danialfarid/ng-file-upload) 24 | setup instructions as well. 25 | 26 | ### Upload 27 | 28 | You can now [capture file input to an angular model](https://gist.github.com/thenikso/8899bc6b760e094dd2b5) 29 | and then, in your controller: 30 | 31 | ```javascript 32 | angular 33 | // Include the angular-cloudinary module 34 | .module('myModule', ['angular-cloudinary']) 35 | // Configure the cloudinary service 36 | .config(function (cloudinaryProvider) { 37 | cloudinaryProvider.config({ 38 | upload_endpoint: 'https://api.cloudinary.com/v1_1/', // default 39 | cloud_name: 'my_cloudinary_cloud_name', // required 40 | upload_preset: 'my_preset', // optional 41 | }); 42 | }) 43 | // Inject the `cloudinary` service in your controller 44 | .controller('myController', function($scope, cloudinary) { 45 | // Have a way to see when a file should be uploaded 46 | $scope.$watch('myFile', function(myFile) { 47 | // Use the service to upload the file 48 | cloudinary.upload(myFile, { /* cloudinary options here */ }) 49 | // This returns a promise that can be used for result handling 50 | .then(function (resp) { 51 | alert('all done!'); 52 | }); 53 | }); 54 | }); 55 | ``` 56 | 57 | You might want to use Cloudinary signed presets for security reason. You will need 58 | to generate a signed set of cloudinary options with any of the backend Cloudinary 59 | library and return that to your client AngularJS application so that it can be 60 | fed to `cloudinary.upload`'s options. 61 | 62 | ### Display a Cloudinary image 63 | 64 | To view an image you usually want to use the `cl-src` directive as documented in 65 | [Cloudinary Angular](https://github.com/cloudinary/cloudinary_angular). The version 66 | in this repository has some additions like: 67 | 68 | - better sorting of transformation attributes to generate valid Cloudinary urls 69 | - using `dpr="auto"` will auto-detect the current browser retina pixel ratio 70 | 71 | A full list of available attributes documentation might be available in the future. 72 | 73 | ``` 74 | 81 | ``` 82 | 83 | ## License 84 | 85 | Released under the MIT license. 86 | -------------------------------------------------------------------------------- /angular-cloudinary.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | if (typeof require !== 'undefined') { 5 | require('ng-file-upload'); 6 | } 7 | 8 | var OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; 9 | var AKAMAI_SHARED_CDN = "res.cloudinary.com"; 10 | var SHARED_CDN = AKAMAI_SHARED_CDN; 11 | 12 | var angularModule = angular.module('angular-cloudinary', [ 13 | 'ngFileUpload', 14 | ]); 15 | 16 | var cloudinaryAttr = function(attr){ 17 | if (attr.match(/cl[A-Z]/)) attr = attr.substring(2); 18 | return attr.replace('-', '_').replace(/([a-z])([A-Z])/g,'$1_$2').toLowerCase(); 19 | }; 20 | 21 | ['Src', 'Srcset', 'Href'].forEach(function(attrName) { 22 | var normalized = 'cl' + attrName; 23 | attrName = attrName.toLowerCase(); 24 | angularModule.directive(normalized, ['$sniffer', 'cloudinary', function($sniffer, cloudinary) { 25 | return { 26 | priority: 99, // it needs to run after the attributes are interpolated 27 | link: function(scope, element, attr) { 28 | var propName = attrName, 29 | name = attrName; 30 | 31 | if (attrName === 'href' && 32 | toString.call(element.prop('href')) === '[object SVGAnimatedString]') { 33 | name = 'xlinkHref'; 34 | attr.$attr[name] = 'xlink:href'; 35 | propName = null; 36 | } 37 | 38 | attr.$observe(normalized, function(value) { 39 | if (!value) 40 | return; 41 | 42 | var attributes = {}; 43 | angular.forEach(element[0].attributes, function(v){attributes[cloudinaryAttr(v.name)] = v.value}); 44 | value = cloudinary.url(value, attributes); 45 | attr.$set(name, value); 46 | 47 | // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist 48 | // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need 49 | // to set the property as well to achieve the desired effect. 50 | // we use attr[attrName] value since $set can sanitize the url. 51 | if ($sniffer.msie && propName) element.prop(propName, attr[name]); 52 | }); 53 | } 54 | }; 55 | }]); 56 | }); 57 | 58 | angularModule.directive('clTransformation', [function() { 59 | return { 60 | restrict : 'E', 61 | transclude : false, 62 | require: '^clImage', 63 | link : function (scope, element, attrs, clImageCtrl) { 64 | var attributes = {}; 65 | angular.forEach(attrs, function(value, name){ 66 | if (name[0] !== '$') { 67 | attributes[cloudinaryAttr(name)] = value; 68 | } 69 | }); 70 | clImageCtrl.addTransformation(attributes); 71 | } 72 | } 73 | }]); 74 | 75 | angularModule.directive('clImage', ['cloudinary', function(cloudinary) { 76 | var Controller = function($scope) { 77 | this.addTransformation = function(ts) { 78 | $scope.transformations = $scope.transformations || []; 79 | $scope.transformations.push(ts); 80 | } 81 | }; 82 | Controller.$inject = ['$scope']; 83 | return { 84 | restrict : 'E', 85 | replace: true, 86 | transclude : true, 87 | template: "", 88 | scope: {}, 89 | priority: 99, 90 | controller: Controller, 91 | // The linking function will add behavior to the template 92 | link : function(scope, element, attrs) { 93 | var attributes = {}; 94 | var publicId = null; 95 | 96 | angular.forEach(attrs, function(value, name){attributes[cloudinaryAttr(name)] = value}); 97 | 98 | if (scope.transformations) { 99 | attributes.transformation = scope.transformations; 100 | } 101 | 102 | // store public id and load image 103 | attrs.$observe('publicId', function(value){ 104 | if (!value) return; 105 | publicId = value 106 | loadImage(); 107 | }); 108 | 109 | // observe and update version attribute 110 | attrs.$observe('version', function(value){ 111 | if (!value) return; 112 | attributes['version'] = value; 113 | loadImage(); 114 | }); 115 | 116 | if (attrs.htmlWidth) { 117 | element.attr("width", attrs.htmlWidth); 118 | } else { 119 | element.removeAttr("width"); 120 | } 121 | if (attrs.htmlHeight) { 122 | element.attr("height", attrs.htmlHeight); 123 | } else { 124 | element.removeAttr("height"); 125 | } 126 | 127 | var loadImage = function() { 128 | var url = cloudinary.url(publicId, attributes); 129 | element.attr('src', url); 130 | } 131 | 132 | } 133 | }; 134 | }]); 135 | 136 | angularModule.directive('clVideo', ['cloudinary', function(cloudinary) { 137 | return { 138 | restrict: 'E', 139 | scope: {}, 140 | priority: 99, 141 | replace: true, 142 | transclude: true, 143 | template: '
', 144 | link: function (scope, element, attrs) { 145 | var attributes = {}; 146 | var publicId = null; 147 | 148 | angular.forEach(attrs, function(value, name) { 149 | var key = cloudinaryAttr(name); 150 | if (key[0] === '$') return; 151 | attributes[key] = value; 152 | }); 153 | 154 | if (scope.transformations) { 155 | attributes.transformation = scope.transformations; 156 | } 157 | 158 | attrs.$observe('publicId', function(value){ 159 | if (!value) return; 160 | publicId = value; 161 | loadVideo(); 162 | }); 163 | 164 | function loadVideo () { 165 | var videoElement = cloudinary.video(publicId, attributes); 166 | element.empty().append(videoElement); 167 | } 168 | } 169 | } 170 | }]); 171 | 172 | angularModule.filter('clUrl', ['cloudinary', function(cloudinary) { 173 | return function (input, options) { 174 | return cloudinary.url(input, options || {}); 175 | } 176 | }]); 177 | 178 | angularModule.provider('cloudinary', function () { 179 | var config = { 180 | upload_endpoint: 'https://api.cloudinary.com/v1_1/', 181 | resolveAutoDpr: null, 182 | }; 183 | 184 | this.config = function (obj) { 185 | angular.extend(config, obj); 186 | }; 187 | 188 | this.$get = ['Upload', function (Upload) { 189 | return { 190 | url: cloudinary_url, 191 | upload: upload, 192 | video: createVideoElement, 193 | }; 194 | 195 | function upload (file, options) { 196 | var cloud_name = options.cloud_name || config.cloud_name 197 | if (config.upload_preset) { 198 | options = angular.extend({ 199 | upload_preset: config.upload_preset 200 | }, options); 201 | } 202 | return Upload.upload({ 203 | url: config.upload_endpoint + cloud_name + '/upload', 204 | fields: options, 205 | file: file 206 | }); 207 | } 208 | }]; 209 | 210 | function cloudinary_url(public_id, options) { 211 | if (!public_id) { 212 | return ''; 213 | } 214 | options = options || {}; 215 | var type = option_consume(options, 'type', 'upload'); 216 | if (type == 'fetch') { 217 | options.fetch_format = options.fetch_format || option_consume(options, 'format'); 218 | } 219 | var transformation = generate_transformation_string(options); 220 | var resource_type = option_consume(options, 'resource_type', "image"); 221 | var version = option_consume(options, 'version'); 222 | var format = option_consume(options, 'format'); 223 | var cloud_name = option_consume(options, 'cloud_name', config.cloud_name); 224 | if (!cloud_name) throw "Unknown cloud_name"; 225 | var private_cdn = option_consume(options, 'private_cdn', config.private_cdn); 226 | var secure_distribution = option_consume(options, 'secure_distribution', config.secure_distribution); 227 | var cname = option_consume(options, 'cname', config.cname); 228 | var cdn_subdomain = option_consume(options, 'cdn_subdomain', config.cdn_subdomain); 229 | var secure_cdn_subdomain = option_consume(options, 'secure_cdn_subdomain', config.secure_cdn_subdomain); 230 | var shorten = option_consume(options, 'shorten', config.shorten); 231 | var secure = option_consume(options, 'secure', window.location.protocol == 'https:'); 232 | var protocol = option_consume(options, 'protocol', config.protocol); 233 | var trust_public_id = option_consume(options, 'trust_public_id'); 234 | var url_suffix = option_consume(options, 'url_suffix'); 235 | var use_root_path = option_consume(options, 'use_root_path', config.use_root_path); 236 | if (!private_cdn) { 237 | if (url_suffix) throw "URL Suffix only supported in private CDN"; 238 | if (use_root_path) throw "Root path only supported in private CDN"; 239 | } 240 | 241 | if (type == 'fetch') { 242 | public_id = absolutize(public_id); 243 | } 244 | 245 | if (public_id.search("/") >= 0 && !public_id.match(/^v[0-9]+/) && !public_id.match(/^https?:\//) && !present(version)) { 246 | version = 0; 247 | } 248 | 249 | if (public_id.match(/^https?:/)) { 250 | if (type == "upload" || type == "asset") return public_id; 251 | public_id = encodeURIComponent(public_id).replace(/%3A/g, ":").replace(/%2F/g, "/"); 252 | } else { 253 | // Make sure public_id is URI encoded. 254 | public_id = encodeURIComponent(decodeURIComponent(public_id)).replace(/%3A/g, ":").replace(/%2F/g, "/"); 255 | if (url_suffix) { 256 | if (url_suffix.match(/[\.\/]/)) throw "url_suffix should not include . or /"; 257 | public_id = public_id + "/" + url_suffix; 258 | } 259 | 260 | if (format) { 261 | if (!trust_public_id) public_id = public_id.replace(/\.(jpg|png|gif|webp)$/, ''); 262 | public_id = public_id + "." + format; 263 | } 264 | } 265 | 266 | var resource_type_and_type = finalize_resource_type(resource_type, type, url_suffix, use_root_path, shorten); 267 | 268 | var prefix = cloudinary_url_prefix(public_id, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution, protocol); 269 | 270 | var url = [prefix, resource_type_and_type, transformation, version ? "v" + version : "", 271 | public_id].join("/").replace(/([^:])\/+/g, '$1/'); 272 | return url; 273 | } 274 | 275 | function createVideoElement (public_id, options) { 276 | options = options || {}; 277 | public_id = public_id.replace(/\.(mp4|ogv|webm)$/, ''); 278 | var source_types = option_consume(options, 'source_types', []); 279 | var source_transformation = option_consume(options, 'source_transformation', {}); 280 | var fallback = option_consume(options, 'fallback_content', ''); 281 | 282 | if (source_types.length == 0) source_types = DEFAULT_VIDEO_SOURCE_TYPES; 283 | var video_options = angular.copy(options); 284 | 285 | if (video_options.hasOwnProperty('poster')) { 286 | if (angular.isObject(video_options.poster)) { 287 | if (video_options.poster.hasOwnProperty('public_id')) { 288 | video_options.poster = cloudinary_url(video_options.poster.public_id, video_options.poster); 289 | } else { 290 | video_options.poster = cloudinary_url(public_id, angular.extend({}, DEFAULT_POSTER_OPTIONS, video_options.poster)); 291 | } 292 | } 293 | } else { 294 | video_options.poster = cloudinary_url(public_id, angular.extend({}, DEFAULT_POSTER_OPTIONS, options)); 295 | } 296 | 297 | if (!video_options.poster) delete video_options.poster; 298 | 299 | var html = ''; 325 | return html; 326 | } 327 | 328 | function option_consume(options, option_name, default_value) { 329 | var result = options[option_name]; 330 | delete options[option_name]; 331 | return typeof(result) == 'undefined' ? default_value : result; 332 | } 333 | 334 | function generate_transformation_string(options) { 335 | var base_transformations = process_base_transformations(options); 336 | process_size(options); 337 | process_html_dimensions(options); 338 | 339 | var params = []; 340 | for (var param in TRANSFORMATION_PARAM_NAME_MAPPING) { 341 | var value = option_consume(options, param); 342 | if (!present(value)) continue; 343 | if (TRANSFORMATION_PARAM_VALUE_MAPPING[param]) { 344 | value = TRANSFORMATION_PARAM_VALUE_MAPPING[param](value); 345 | } 346 | if (!present(value)) continue; 347 | if (angular.isArray(value)) { 348 | var actual = []; 349 | for (var i = 0; i < value.length; i++) { 350 | actual.push(TRANSFORMATION_PARAM_NAME_MAPPING[param] + "_" + value[i]); 351 | } 352 | params.push(actual.join(',')); 353 | } 354 | else { 355 | params.push(TRANSFORMATION_PARAM_NAME_MAPPING[param] + "_" + value); 356 | } 357 | } 358 | // params.sort(); 359 | 360 | var raw_transformation = option_consume(options, 'raw_transformation'); 361 | if (present(raw_transformation)) params.push(raw_transformation); 362 | var transformation = params.join(","); 363 | if (present(transformation)) base_transformations.push(transformation); 364 | return base_transformations.join("/"); 365 | } 366 | 367 | function process_base_transformations(options) { 368 | var transformations = build_array(options.transformation); 369 | var all_named = true; 370 | for (var i = 0; i < transformations.length; i++) { 371 | all_named = all_named && typeof(transformations[i]) == 'string'; 372 | } 373 | if (all_named) { 374 | return []; 375 | } 376 | delete options.transformation; 377 | var base_transformations = []; 378 | for (var i = 0; i < transformations.length; i++) { 379 | var transformation = transformations[i]; 380 | if (typeof(transformation) == 'string') { 381 | base_transformations.push("t_" + transformation); 382 | } else { 383 | base_transformations.push(generate_transformation_string(angular.extend({}, transformation))); 384 | } 385 | } 386 | return base_transformations; 387 | } 388 | 389 | function build_array(arg) { 390 | if (arg === null || typeof(arg) == 'undefined') { 391 | return []; 392 | } else if (angular.isArray(arg)) { 393 | return arg; 394 | } else if (angular.isString(arg)) { 395 | return arg.split(','); 396 | } else { 397 | return [arg]; 398 | } 399 | } 400 | 401 | function process_size(options) { 402 | var size = option_consume(options, 'size'); 403 | if (size === 'fitScreen') { 404 | options.width = Math.min(1440, Math.max(screen.width, screen.height)); 405 | } 406 | else if (size) { 407 | var split_size = size.split("x"); 408 | options.width = split_size[0]; 409 | options.height = split_size[1]; 410 | } 411 | } 412 | 413 | function process_html_dimensions(options) { 414 | var width = options.width, height = options.height; 415 | var has_layer = options.overlay || options.underlay; 416 | var crop = options.crop; 417 | var use_as_html_dimensions = !has_layer && !options.angle && crop != "fit" && crop != "limit" && crop != "lfill"; 418 | if (use_as_html_dimensions) { 419 | if (width && !options.html_width && width !== "auto" && parseFloat(width) >= 1) options.html_width = width; 420 | if (height && !options.html_height && parseFloat(height) >= 1) options.html_height = height; 421 | } 422 | if (!crop && !has_layer) { 423 | delete options.width; 424 | delete options.height; 425 | } 426 | } 427 | 428 | function html_attrs(attrs) { 429 | var pairs = []; 430 | angular.forEach(attrs, function(value, key) { 431 | pairs.push(join_pair(key, value)); 432 | }); 433 | pairs.sort(); 434 | return pairs.filter(function(pair){return pair;}).join(" "); 435 | } 436 | 437 | function join_pair(key, value) { 438 | if (!value) { 439 | return undefined; 440 | } else if (value === true) { 441 | return key; 442 | } else { 443 | return key + "=\"" + value + "\""; 444 | } 445 | } 446 | 447 | var TRANSFORMATION_PARAM_NAME_MAPPING = { 448 | width: 'w', 449 | height: 'h', 450 | angle: 'a', 451 | background: 'b', 452 | border: 'bo', 453 | color: 'co', 454 | color_space: 'cs', 455 | crop: 'c', 456 | default_image: 'd', 457 | delay: 'dl', 458 | density: 'dn', 459 | dpr: 'dpr', 460 | effect: 'e', 461 | fetch_format: 'f', 462 | flags: 'fl', 463 | opacity: 'o', 464 | overlay: 'l', 465 | page: 'pg', 466 | prefix: 'p', 467 | quality: 'q', 468 | radius: 'r', 469 | transformation: 't', 470 | underlay: 'u', 471 | gravity: 'g', 472 | x: 'x', 473 | y: 'y' 474 | }; 475 | 476 | var TRANSFORMATION_PARAM_VALUE_MAPPING = { 477 | angle: function(angle){ return build_array(angle).join("."); }, 478 | background: function(background) { return background.replace(/^#/, 'rgb:');}, 479 | border: function(border) { 480 | if (border != null && Object.prototype.toString.call(border) === "[object Object]") { 481 | var border_width = "" + (border.width || 2); 482 | var border_color = (border.color || "black").replace(/^#/, 'rgb:'); 483 | border = border_width + "px_solid_" + border_color; 484 | } 485 | return border; 486 | }, 487 | color: function(color) { return color.replace(/^#/, 'rgb:');}, 488 | dpr: function(dpr) { 489 | dpr = dpr.toString(); 490 | if (dpr === "auto") { 491 | var suggestedDpr; 492 | if (window.devicePixelRatio) { 493 | suggestedDpr = Math.round(window.devicePixelRatio).toFixed(1); 494 | } 495 | else if (isRetina()) { 496 | suggestedDpr = '2.0'; 497 | } 498 | else { 499 | suggestedDpr = '1.0'; 500 | } 501 | if (typeof config.resolveAutoDpr !== 'function') { 502 | return suggestedDpr; 503 | } 504 | return config.resolveAutoDpr(suggestedDpr); 505 | } else if (dpr.match(/^\d+$/)) { 506 | return dpr + ".0"; 507 | } else { 508 | return dpr; 509 | } 510 | }, 511 | effect: function(effect) { return build_array(effect); }, 512 | flags: function(flags) { return build_array(flags); }, 513 | transformation: function(transformation) { return build_array(transformation).join(".")} 514 | }; 515 | 516 | var DEFAULT_POSTER_OPTIONS = { format: 'jpg', resource_type: 'video' }; 517 | 518 | var DEFAULT_VIDEO_SOURCE_TYPES = ['webm', 'mp4', 'ogv']; 519 | 520 | function isRetina(){ 521 | return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx), only screen and (min-resolution: 75.6dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min--moz-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)').matches)) || (window.devicePixelRatio && window.devicePixelRatio > 2)) && /(iPad|iPhone|iPod)/g.test(navigator.userAgent); 522 | } 523 | 524 | function present(value) { 525 | return typeof value != 'undefined' && ("" + value).length > 0; 526 | } 527 | 528 | function finalize_resource_type(resource_type, type, url_suffix, use_root_path, shorten) { 529 | var resource_type_and_type = resource_type + "/" + type; 530 | if (url_suffix) { 531 | if (resource_type_and_type == "image/upload") { 532 | resource_type_and_type = "images"; 533 | } else if (resource_type_and_type == "raw/upload") { 534 | resource_type_and_type = "files"; 535 | } else { 536 | throw "URL Suffix only supported for image/upload and raw/upload"; 537 | } 538 | } 539 | if (use_root_path) { 540 | if (resource_type_and_type == "image/upload" || resource_type_and_type == "images") { 541 | resource_type_and_type = ""; 542 | } else { 543 | throw "Root path only supported for image/upload"; 544 | } 545 | } 546 | if (shorten && resource_type_and_type == "image/upload") { 547 | resource_type_and_type = "iu"; 548 | } 549 | return resource_type_and_type; 550 | } 551 | 552 | function cloudinary_url_prefix(public_id, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution, protocol) { 553 | if (cloud_name.match(/^\//) && !secure) { 554 | return "/res" + cloud_name; 555 | } 556 | 557 | var prefix = secure ? 'https://' : (window.location.protocol === 'file:' ? "file://" : 'http://'); 558 | prefix = protocol ? protocol + '//' : prefix; 559 | 560 | var shared_domain = !private_cdn; 561 | if (secure) { 562 | if (!secure_distribution || secure_distribution == OLD_AKAMAI_SHARED_CDN) { 563 | secure_distribution = private_cdn ? cloud_name + "-res.cloudinary.com" : SHARED_CDN; 564 | } 565 | shared_domain = shared_domain || secure_distribution == SHARED_CDN; 566 | if (secure_cdn_subdomain == null && shared_domain) { 567 | secure_cdn_subdomain = cdn_subdomain; 568 | } 569 | if (secure_cdn_subdomain) { 570 | secure_distribution = secure_distribution.replace('res.cloudinary.com', "res-" + ((crc32(public_id) % 5) + 1) + ".cloudinary.com"); 571 | } 572 | prefix += secure_distribution; 573 | } else if (cname) { 574 | var subdomain = cdn_subdomain ? "a" + ((crc32(public_id) % 5) + 1) + "." : ""; 575 | prefix += subdomain + cname; 576 | } else { 577 | prefix += (private_cdn ? cloud_name + "-res" : "res"); 578 | prefix += (cdn_subdomain ? "-" + ((crc32(public_id) % 5) + 1) : "") 579 | prefix += ".cloudinary.com"; 580 | } 581 | if (shared_domain) prefix += "/" + cloud_name; 582 | 583 | return prefix; 584 | } 585 | }); 586 | 587 | if (typeof module !== 'undefined' && module && module.exports) { 588 | module.exports = angularModule; 589 | } 590 | 591 | return angularModule; 592 | 593 | })(); 594 | --------------------------------------------------------------------------------