PageSourceSearch

https://muahang.hachina.vn/static/js/cloudinary/jquery.cloudinary.js?v=1790900718

js hachina.vn collected 2026-10-02 00:26:30 UTC 152,351 bytes, 4,736 lines download raw bytes

1
2/**
3 * Cloudinary's JavaScript library - Version 2.5.0
4 * Copyright Cloudinary
5 * see https://github.com/cloudinary/cloudinary_js
6 *
7 */
8var slice = [].slice,
9  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
10  hasProp = {}.hasOwnProperty;
11
12(function(root, factory) {
13  var name, ref, results, value;
14  if ((typeof define === 'function') && define.amd) {
15    return define(['jquery'], factory);
16  } else if (typeof exports === 'object') {
17    return module.exports = factory(require('jquery'));
18  } else {
19    root.cloudinary || (root.cloudinary = {});
20    ref = factory(jQuery);
21    results = [];
22    for (name in ref) {
23      value = ref[name];
24      results.push(root.cloudinary[name] = value);
25    }
26    return results;
27  }
28})(this, function(jQuery) {
29
30  /*
31   * Includes common utility methods and shims
32   */
33
34  /**
35   * Return true if all items in list are strings
36   * @function Util.allString
37   * @param {Array} list - an array of items
38   */
39  var ArrayParam, BaseUtil, ClientHintsMetaTag, Cloudinary, CloudinaryJQuery, Condition, Configuration, Expression, ExpressionParam, FetchLayer, HtmlTag, ImageTag, Layer, LayerParam, Param, RangeParam, RawParam, SubtitlesLayer, TextLayer, Transformation, TransformationBase, TransformationParam, Util, VideoTag, addClass, allStrings, base64Encode, base64EncodeURL, camelCase, cloneDeep, cloudinary, compact, contains, convertKeys, crc32, defaults, difference, funcTag, functions, getAttribute, getData, hasClass, identity, isEmpty, isFunction, isNumberLike, isObject, isString, m, merge, objToString, objectProto, parameters, reWords, removeAttribute, setAttribute, setAttributes, setData, smartEscape, snakeCase, utf8_encode, webp, width, withCamelCaseKeys, withSnakeCaseKeys, without;
40  allStrings = function(list) {
41    var item, j, len;
42    for (j = 0, len = list.length; j < len; j++) {
43      item = list[j];
44      if (!Util.isString(item)) {
45        return false;
46      }
47    }
48    return true;
49  };
50
51  /**
52  * Creates a new array without the given item.
53  * @function Util.without
54  * @param {Array} array - original array
55  * @param {*} item - the item to exclude from the new array
56  * @return {Array} a new array made of the original array's items except for `item`
57   */
58  without = function(array, item) {
59    var i, length, newArray;
60    newArray = [];
61    i = -1;
62    length = array.length;
63    while (++i < length) {
64      if (array[i] !== item) {
65        newArray.push(array[i]);
66      }
67    }
68    return newArray;
69  };
70
71  /**
72  * Return true is value is a number or a string representation of a number.
73  * @function Util.isNumberLike
74  * @param {*} value
75  * @returns {boolean} true if value is a number
76  * @example
77  *    Util.isNumber(0) // true
78  *    Util.isNumber("1.3") // true
79  *    Util.isNumber("") // false
80  *    Util.isNumber(undefined) // false
81   */
82  isNumberLike = function(value) {
83    return (value != null) && !isNaN(parseFloat(value));
84  };
85
86  /**
87   * Escape all characters matching unsafe in the given string
88   * @function Util.smartEscape
89   * @param {string} string - source string to escape
90   * @param {RegExp} unsafe - characters that must be escaped
91   * @return {string} escaped string
92   */
93  smartEscape = function(string, unsafe) {
94    if (unsafe == null) {
95      unsafe = /([^a-zA-Z0-9_.\-\/:]+)/g;
96    }
97    return string.replace(unsafe, function(match) {
98      return match.split("").map(function(c) {
99        return "%" + c.charCodeAt(0).toString(16).toUpperCase();
100      }).join("");
101    });
102  };
103
104  /**
105   * Assign values from sources if they are not defined in the destination.
106   * Once a value is set it does not change
107   * @function Util.defaults
108   * @param {Object} destination - the object to assign defaults to
109   * @param {...Object} source - the source object(s) to assign defaults from
110   * @return {Object} destination after it was modified
111   */
112  defaults = function() {
113    var destination, sources;
114    destination = arguments[0], sources = 2 <= arguments.length ? slice.call(arguments, 1) : [];
115    return sources.reduce(function(dest, source) {
116      var key, value;
117      for (key in source) {
118        value = source[key];
119        if (dest[key] === void 0) {
120          dest[key] = value;
121        }
122      }
123      return dest;
124    }, destination);
125  };
126
127  /*********** lodash functions */
128  objectProto = Object.prototype;
129
130  /**
131   * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
132   * of values.
133   */
134  objToString = objectProto.toString;
135
136  /**
137   * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
138   * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
139   *
140   * @param {*} value The value to check.
141   * @returns {boolean} Returns `true` if `value` is an object, else `false`.
142   * @example
143   *
144  #isObject({});
145   * // => true
146   *
147  #isObject([1, 2, 3]);
148   * // => true
149   *
150  #isObject(1);
151   * // => false
152   */
153  isObject = function(value) {
154    var type;
155    type = typeof value;
156    return !!value && (type === 'object' || type === 'function');
157  };
158  funcTag = '[object Function]';
159
160  /**
161  * Checks if `value` is classified as a `Function` object.
162  * @function Util.isFunction
163  * @param {*} value The value to check.
164  * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
165  * @example
166  *
167  * function Foo(){};  
168  * isFunction(Foo);
169  * // => true
170  *
171  * isFunction(/abc/);
172  * // => false
173   */
174  isFunction = function(value) {
175    return isObject(value) && objToString.call(value) === funcTag;
176  };
177
178  /*********** lodash functions */
179
180  /** Used to match words to create compound words. */
181  reWords = (function() {
182    var lower, upper;
183    upper = '[A-Z]';
184    lower = '[a-z]+';
185    return RegExp(upper + '+(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g');
186  })();
187
188  /**
189  * Convert string to camelCase
190  * @function Util.camelCase
191  * @param {string} string - the string to convert
192  * @return {string} in camelCase format
193   */
194  camelCase = function(source) {
195    var i, word, words;
196    words = source.match(reWords);
197    words = (function() {
198      var j, len, results;
199      results = [];
200      for (i = j = 0, len = words.length; j < len; i = ++j) {
201        word = words[i];
202        word = word.toLocaleLowerCase();
203        if (i) {
204          results.push(word.charAt(0).toLocaleUpperCase() + word.slice(1));
205        } else {
206          results.push(word);
207        }
208      }
209      return results;
210    })();
211    return words.join('');
212  };
213
214  /**
215   * Convert string to snake_case
216   * @function Util.snakeCase
217   * @param {string} string - the string to convert
218   * @return {string} in snake_case format
219   */
220  snakeCase = function(source) {
221    var i, word, words;
222    words = source.match(reWords);
223    words = (function() {
224      var j, len, results;
225      results = [];
226      for (i = j = 0, len = words.length; j < len; i = ++j) {
227        word = words[i];
228        results.push(word.toLocaleLowerCase());
229      }
230      return results;
231    })();
232    return words.join('_');
233  };
234  convertKeys = function(source, converter) {
235    var key, result, value;
236    if (converter == null) {
237      converter = Util.identity;
238    }
239    result = {};
240    for (key in source) {
241      value = source[key];
242      key = converter(key);
243      if (!Util.isEmpty(key)) {
244        result[key] = value;
245      }
246    }
247    return result;
248  };
249
250  /**
251   * Create a copy of the source object with all keys in camelCase
252   * @function Util.withCamelCaseKeys
253   * @param {Object} value - the object to copy
254   * @return {Object} a new object
255   */
256  withCamelCaseKeys = function(source) {
257    return convertKeys(source, Util.camelCase);
258  };
259
260  /**
261   * Create a copy of the source object with all keys in snake_case
262   * @function Util.withSnakeCaseKeys
263   * @param {Object} value - the object to copy
264   * @return {Object} a new object
265   */
266  withSnakeCaseKeys = function(source) {
267    return convertKeys(source, Util.snakeCase);
268  };
269  base64Encode = typeof btoa !== 'undefined' && isFunction(btoa) ? btoa : typeof Buffer !== 'undefined' && isFunction(Buffer) ? function(input) {
270    if (!(input instanceof Buffer)) {
271      input = new Buffer.from(String(input), 'binary');
272    }
273    return input.toString('base64');
274  } : function(input) {
275    throw new Error("No base64 encoding function found");
276  };
277
278  /**
279  * Returns the Base64-decoded version of url.<br>
280  * This method delegates to `btoa` if present. Otherwise it tries `Buffer`.
281  * @function Util.base64EncodeURL
282  * @param {string} url - the url to encode. the value is URIdecoded and then re-encoded before converting to base64 representation
283  * @return {string} the base64 representation of the URL
284   */
285  base64EncodeURL = function(input) {
286    var error1, ignore;
287    try {
288      input = decodeURI(input);
289    } catch (error1) {
290      ignore = error1;
291    }
292    input = encodeURI(input);
293    return base64Encode(input);
294  };
295  BaseUtil = {
296    allStrings: allStrings,
297    camelCase: camelCase,
298    convertKeys: convertKeys,
299    defaults: defaults,
300    snakeCase: snakeCase,
301    without: without,
302    isFunction: isFunction,
303    isNumberLike: isNumberLike,
304    smartEscape: smartEscape,
305    withCamelCaseKeys: withCamelCaseKeys,
306    withSnakeCaseKeys: withSnakeCaseKeys,
307    base64EncodeURL: base64EncodeURL
308  };
309
310  /**
311    * Includes utility methods and lodash / jQuery shims
312   */
313
314  /**
315    * Get data from the DOM element.
316    *
317    * This method will use jQuery's `data()` method if it is available, otherwise it will get the `data-` attribute
318    * @param {Element} element - the element to get the data from
319    * @param {string} name - the name of the data item
320    * @returns the value associated with the `name`
321    * @function Util.getData
322   */
323  getData = function(element, name) {
324    return jQuery(element).data(name);
325  };
326
327  /**
328    * Set data in the DOM element.
329    *
330    * This method will use jQuery's `data()` method if it is available, otherwise it will set the `data-` attribute
331    * @function Util.setData
332    * @param {Element} element - the element to set the data in
333    * @param {string} name - the name of the data item
334    * @param {*} value - the value to be set
335    *
336   */
337  setData = function(element, name, value) {
338    return jQuery(element).data(name, value);
339  };
340
341  /**
342    * Get attribute from the DOM element.
343    *
344    * This method will use jQuery's `attr()` method if it is available, otherwise it will get the attribute directly
345    * @function Util.getAttribute
346    * @param {Element} element - the element to set the attribute for
347    * @param {string} name - the name of the attribute
348    * @returns {*} the value of the attribute
349    *
350   */
351  getAttribute = function(element, name) {
352    return jQuery(element).attr(name);
353  };
354
355  /**
356    * Set attribute in the DOM element.
357    *
358    * This method will use jQuery's `attr()` method if it is available, otherwise it will set the attribute directly
359    * @function Util.setAttribute
360    * @param {Element} element - the element to set the attribute for
361    * @param {string} name - the name of the attribute
362    * @param {*} value - the value to be set
363   */
364  setAttribute = function(element, name, value) {
365    return jQuery(element).attr(name, value);
366  };
367
368  /**
369   * Remove an attribute in the DOM element.
370   *
371   * @function Util.removeAttribute
372   * @param {Element} element - the element to set the attribute for
373   * @param {string} name - the name of the attribute
374   */
375  removeAttribute = function(element, name) {
376    return jQuery(element).removeAttr(name);
377  };
378
379  /**
380    * Set a group of attributes to the element
381    * @function Util.setAttributes
382    * @param {Element} element - the element to set the attributes for
383    * @param {Object} attributes - a hash of attribute names and values
384   */
385  setAttributes = function(element, attributes) {
386    return jQuery(element).attr(attributes);
387  };
388
389  /**
390    * Checks if element has a css class
391    * @function Util.hasClass
392    * @param {Element} element - the element to check
393    * @param {string} name - the class name
394    @returns {boolean} true if the element has the class
395   */
396  hasClass = function(element, name) {
397    return jQuery(element).hasClass(name);
398  };
399
400  /**
401    * Add class to the element
402    * @function Util.addClass
403    * @param {Element} element - the element
404    * @param {string} name - the class name to add
405   */
406  addClass = function(element, name) {
407    return jQuery(element).addClass(name);
408  };
409  width = function(element) {
410    return jQuery(element).width();
411  };
412
413  /**
414   * Returns true if item is empty:
415   * <ul>
416   *   <li>item is null or undefined</li>
417   *   <li>item is an array or string of length 0</li>
418   *   <li>item is an object with no keys</li>
419   * </ul>
420   * @function Util.isEmpty
421   * @param item
422   * @returns {boolean} true if item is empty
423   */
424  isEmpty = function(item) {
425    return (item == null) || (jQuery.isArray(item) || Util.isString(item)) && item.length === 0 || (jQuery.isPlainObject(item) && jQuery.isEmptyObject(item));
426  };
427
428  /**
429   * Returns true if item is a string
430   * @param item
431   * @returns {boolean} true if item is a string
432   */
433  isString = function(item) {
434    return typeof item === 'string' || (item != null ? item.toString() : void 0) === '[object String]';
435  };
436
437  /**
438   * Recursively assign source properties to destination
439   * @function Util.merge
440   * @param {Object} destination - the object to assign to
441   * @param {...Object} [sources] The source objects.
442   */
443  merge = function() {
444    var args, i;
445    args = (function() {
446      var j, len, results;
447      results = [];
448      for (j = 0, len = arguments.length; j < len; j++) {
449        i = arguments[j];
450        results.push(i);
451      }
452      return results;
453    }).apply(this, arguments);
454    args.unshift(true);
455    return jQuery.extend.apply(this, args);
456  };
457
458  /**
459   * Creates a new array from the parameter with "falsey" values removed
460   * @function Util.compact
461   * @param {Array} array - the array to remove values from
462   * @return {Array} a new array without falsey values
463   */
464  compact = function(arr) {
465    var item, j, len, results;
466    results = [];
467    for (j = 0, len = arr.length; j < len; j++) {
468      item = arr[j];
469      if (item) {
470        results.push(item);
471      }
472    }
473    return results;
474  };
475
476  /**
477   * Create a new copy of the given object, including all internal objects.
478   * @function Util.cloneDeep
479   * @param {Object} value - the object to clone
480   * @return {Object} a new deep copy of the object
481   */
482  cloneDeep = function() {
483    var args;
484    args = jQuery.makeArray(arguments);
485    args.unshift({});
486    args.unshift(true);
487    return jQuery.extend.apply(this, args);
488  };
489
490  /**
491   * Check if a given item is included in the given array
492   * @function Util.contains
493   * @param {Array} array - the array to search in
494   * @param {*} item - the item to search for
495   * @return {boolean} true if the item is included in the array
496   */
497  contains = function(arr, item) {
498    var i, j, len;
499    for (j = 0, len = arr.length; j < len; j++) {
500      i = arr[j];
501      if (i === item) {
502        return true;
503      }
504    }
505    return false;
506  };
507
508  /**
509   * Returns values in the given array that are not included in the other array
510   * @function Util.difference
511   * @param {Array} arr - the array to select from
512   * @param {Array} values - values to filter from arr
513   * @return {Array} the filtered values
514   */
515  difference = function(arr, values) {
516    var item, j, len, results;
517    results = [];
518    for (j = 0, len = arr.length; j < len; j++) {
519      item = arr[j];
520      if (!contains(values, item)) {
521        results.push(item);
522      }
523    }
524    return results;
525  };
526
527  /**
528   * Returns a list of all the function names in obj
529   * @function Util.functions
530   * @param {Object} object - the object to inspect
531   * @return {Array} a list of functions of object
532   */
533  functions = function(object) {
534    var i, results;
535    results = [];
536    for (i in object) {
537      if (jQuery.isFunction(object[i])) {
538        results.push(i);
539      }
540    }
541    return results;
542  };
543
544  /**
545   * Returns the provided value. This functions is used as a default predicate function.
546   * @function Util.identity
547   * @param {*} value
548   * @return {*} the provided value
549   */
550  identity = function(value) {
551    return value;
552  };
553
554  /**
555   * @class Util
556   */
557  Util = jQuery.extend(BaseUtil, {
558    hasClass: hasClass,
559    addClass: addClass,
560    getAttribute: getAttribute,
561    setAttribute: setAttribute,
562    removeAttribute: removeAttribute,
563    setAttributes: setAttributes,
564    getData: getData,
565    setData: setData,
566    width: width,
567    isString: isString,
568    isArray: jQuery.isArray,
569    isEmpty: isEmpty,
570
571    /**
572     * Assign source properties to destination.
573     * If the property is an object it is assigned as a whole, overriding the destination object.
574     * @function Util.assign
575     * @param {Object} destination - the object to assign to
576     */
577    assign: jQuery.extend,
578    merge: merge,
579    cloneDeep: cloneDeep,
580    compact: compact,
581    contains: contains,
582    difference: difference,
583    functions: functions,
584    identity: identity,
585    isPlainObject: jQuery.isPlainObject,
586
587    /**
588     * Remove leading or trailing spaces from text
589     * @function Util.trim
590     * @param {string} text
591     * @return {string} the `text` without leading or trailing spaces
592     */
593    trim: jQuery.trim
594  });
595
596  /**
597   * UTF8 encoder
598   *
599   */
600  utf8_encode = function(argString) {
601    var c1, enc, end, n, start, string, stringl, utftext;
602    if (argString === null || typeof argString === 'undefined') {
603      return '';
604    }
605    string = argString + '';
606    utftext = '';
607    start = void 0;
608    end = void 0;
609    stringl = 0;
610    start = end = 0;
611    stringl = string.length;
612    n = 0;
613    while (n < stringl) {
614      c1 = string.charCodeAt(n);
615      enc = null;
616      if (c1 < 128) {
617        end++;
618      } else if (c1 > 127 && c1 < 2048) {
619        enc = String.fromCharCode(c1 >> 6 | 192, c1 & 63 | 128);
620      } else {
621        enc = String.fromCharCode(c1 >> 12 | 224, c1 >> 6 & 63 | 128, c1 & 63 | 128);
622      }
623      if (enc !== null) {
624        if (end > start) {
625          utftext += string.slice(start, end);
626        }
627        utftext += enc;
628        start = end = n + 1;
629      }
630      n++;
631    }
632    if (end > start) {
633      utftext += string.slice(start, stringl);
634    }
635    return utftext;
636  };
637
638  /**
639   * CRC32 calculator
640   * Depends on 'utf8_encode'
641   */
642  crc32 = function(str) {
643    var crc, i, iTop, table, x, y;
644    str = utf8_encode(str);
645    table = '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D';
646    crc = 0;
647    x = 0;
648    y = 0;
649    crc = crc ^ -1;
650    i = 0;
651    iTop = str.length;
652    while (i < iTop) {
653      y = (crc ^ str.charCodeAt(i)) & 0xFF;
654      x = '0x' + table.substr(y * 9, 8);
655      crc = crc >>> 8 ^ x;
656      i++;
657    }
658    crc = crc ^ -1;
659    if (crc < 0) {
660      crc += 4294967296;
661    }
662    return crc;
663  };
664  Layer = (function() {
665
666    /**
667     * Layer
668     * @constructor Layer
669     * @param {Object} options - layer parameters
670     */
671    function Layer(options) {
672      this.options = {};
673      if (options != null) {
674        ["resourceType", "type", "publicId", "format"].forEach((function(_this) {
675          return function(key) {
676            var ref;
677            return _this.options[key] = (ref = options[key]) != null ? ref : options[Util.snakeCase(key)];
678          };
679        })(this));
680      }
681    }
682
683    Layer.prototype.resourceType = function(value) {
684      this.options.resourceType = value;
685      return this;
686    };
687
688    Layer.prototype.type = function(value) {
689      this.options.type = value;
690      return this;
691    };
692
693    Layer.prototype.publicId = function(value) {
694      this.options.publicId = value;
695      return this;
696    };
697
698
699    /**
700     * Get the public ID, formatted for layer parameter
701     * @function Layer#getPublicId
702     * @return {String} public ID
703     */
704
705    Layer.prototype.getPublicId = function() {
706      var ref;
707      return (ref = this.options.publicId) != null ? ref.replace(/\//g, ":") : void 0;
708    };
709
710
711    /**
712     * Get the public ID, with format if present
713     * @function Layer#getFullPublicId
714     * @return {String} public ID
715     */
716
717    Layer.prototype.getFullPublicId = function() {
718      if (this.options.format != null) {
719        return this.getPublicId() + "." + this.options.format;
720      } else {
721        return this.getPublicId();
722      }
723    };
724
725    Layer.prototype.format = function(value) {
726      this.options.format = value;
727      return this;
728    };
729
730
731    /**
732     * generate the string representation of the layer
733     * @function Layer#toString
734     */
735
736    Layer.prototype.toString = function() {
737      var components;
738      components = [];
739      if (this.options.publicId == null) {
740        throw "Must supply publicId";
741      }
742      if (!(this.options.resourceType === "image")) {
743        components.push(this.options.resourceType);
744      }
745      if (!(this.options.type === "upload")) {
746        components.push(this.options.type);
747      }
748      components.push(this.getFullPublicId());
749      return Util.compact(components).join(":");
750    };
751
752    return Layer;
753
754  })();
755  FetchLayer = (function(superClass) {
756    extend(FetchLayer, superClass);
757
758
759    /**
760     * @constructor FetchLayer
761     * @param {Object|string} options - layer parameters or a url
762     * @param {string} options.url the url of the image to fetch
763     */
764
765    function FetchLayer(options) {
766      FetchLayer.__super__.constructor.call(this, options);
767      if (Util.isString(options)) {
768        this.options.url = options;
769      } else if (options != null ? options.url : void 0) {
770        this.options.url = options.url;
771      }
772    }
773
774    FetchLayer.prototype.url = function(url) {
775      this.options.url = url;
776      return this;
777    };
778
779
780    /**
781     * generate the string representation of the layer
782     * @function FetchLayer#toString
783     * @return {String}
784     */
785
786    FetchLayer.prototype.toString = function() {
787      return "fetch:" + (cloudinary.Util.base64EncodeURL(this.options.url));
788    };
789
790    return FetchLayer;
791
792  })(Layer);
793  TextLayer = (function(superClass) {
794    extend(TextLayer, superClass);
795
796
797    /**
798     * @constructor TextLayer
799     * @param {Object} options - layer parameters
800     */
801
802    function TextLayer(options) {
803      var keys;
804      TextLayer.__super__.constructor.call(this, options);
805      keys = ["resourceType", "resourceType", "fontFamily", "fontSize", "fontWeight", "fontStyle", "textDecoration", "textAlign", "stroke", "letterSpacing", "lineSpacing", "text"];
806      if (options != null) {
807        keys.forEach((function(_this) {
808          return function(key) {
809            var ref;
810            return _this.options[key] = (ref = options[key]) != null ? ref : options[Util.snakeCase(key)];
811          };
812        })(this));
813      }
814      this.options.resourceType = "text";
815    }
816
817    TextLayer.prototype.resourceType = function(resourceType) {
818      throw "Cannot modify resourceType for text layers";
819    };
820
821    TextLayer.prototype.type = function(type) {
822      throw "Cannot modify type for text layers";
823    };
824
825    TextLayer.prototype.format = function(format) {
826      throw "Cannot modify format for text layers";
827    };
828
829    TextLayer.prototype.fontFamily = function(fontFamily) {
830      this.options.fontFamily = fontFamily;
831      return this;
832    };
833
834    TextLayer.prototype.fontSize = function(fontSize) {
835      this.options.fontSize = fontSize;
836      return this;
837    };
838
839    TextLayer.prototype.fontWeight = function(fontWeight) {
840      this.options.fontWeight = fontWeight;
841      return this;
842    };
843
844    TextLayer.prototype.fontStyle = function(fontStyle) {
845      this.options.fontStyle = fontStyle;
846      return this;
847    };
848
849    TextLayer.prototype.textDecoration = function(textDecoration) {
850      this.options.textDecoration = textDecoration;
851      return this;
852    };
853
854    TextLayer.prototype.textAlign = function(textAlign) {
855      this.options.textAlign = textAlign;
856      return this;
857    };
858
859    TextLayer.prototype.stroke = function(stroke) {
860      this.options.stroke = stroke;
861      return this;
862    };
863
864    TextLayer.prototype.letterSpacing = function(letterSpacing) {
865      this.options.letterSpacing = letterSpacing;
866      return this;
867    };
868
869    TextLayer.prototype.lineSpacing = function(lineSpacing) {
870      this.options.lineSpacing = lineSpacing;
871      return this;
872    };
873
874    TextLayer.prototype.text = function(text) {
875      this.options.text = text;
876      return this;
877    };
878
879
880    /**
881     * generate the string representation of the layer
882     * @function TextLayer#toString
883     * @return {String}
884     */
885
886    TextLayer.prototype.toString = function() {
887      var components, hasPublicId, hasStyle, publicId, re, res, start, style, text, textSource;
888      style = this.textStyleIdentifier();
889      if (this.options.publicId != null) {
890        publicId = this.getFullPublicId();
891      }
892      if (this.options.text != null) {
893        hasPublicId = !Util.isEmpty(publicId);
894        hasStyle = !Util.isEmpty(style);
895        if (hasPublicId && hasStyle || !hasPublicId && !hasStyle) {
896          throw "Must supply either style parameters or a public_id when providing text parameter in a text overlay/underlay, but not both!";
897        }
898        re = /\$\([a-zA-Z]\w*\)/g;
899        start = 0;
900        textSource = Util.smartEscape(this.options.text, /[,\/]/g);
901        text = "";
902        while (res = re.exec(textSource)) {
903          text += Util.smartEscape(textSource.slice(start, res.index));
904          text += res[0];
905          start = res.index + res[0].length;
906        }
907        text += Util.smartEscape(textSource.slice(start));
908      }
909      components = [this.options.resourceType, style, publicId, text];
910      return Util.compact(components).join(":");
911    };
912
913    TextLayer.prototype.textStyleIdentifier = function() {
914      var components;
915      components = [];
916      if (this.options.fontWeight !== "normal") {
917        components.push(this.options.fontWeight);
918      }
919      if (this.options.fontStyle !== "normal") {
920        components.push(this.options.fontStyle);
921      }
922      if (this.options.textDecoration !== "none") {
923        components.push(this.options.textDecoration);
924      }
925      components.push(this.options.textAlign);
926      if (this.options.stroke !== "none") {
927        components.push(this.options.stroke);
928      }
929      if (!(Util.isEmpty(this.options.letterSpacing) && !Util.isNumberLike(this.options.letterSpacing))) {
930        components.push("letter_spacing_" + this.options.letterSpacing);
931      }
932      if (!(Util.isEmpty(this.options.lineSpacing) && !Util.isNumberLike(this.options.lineSpacing))) {
933        components.push("line_spacing_" + this.options.lineSpacing);
934      }
935      if (!Util.isEmpty(Util.compact(components))) {
936        if (Util.isEmpty(this.options.fontFamily)) {
937          throw "Must supply fontFamily. " + components;
938        }
939        if (Util.isEmpty(this.options.fontSize) && !Util.isNumberLike(this.options.fontSize)) {
940          throw "Must supply fontSize.";
941        }
942      }
943      components.unshift(this.options.fontFamily, this.options.fontSize);
944      components = Util.compact(components).join("_");
945      return components;
946    };
947
948    return TextLayer;
949
950  })(Layer);
951  SubtitlesLayer = (function(superClass) {
952    extend(SubtitlesLayer, superClass);
953
954
955    /**
956     * Represent a subtitles layer
957     * @constructor SubtitlesLayer
958     * @param {Object} options - layer parameters
959     */
960
961    function SubtitlesLayer(options) {
962      SubtitlesLayer.__super__.constructor.call(this, options);
963      this.options.resourceType = "subtitles";
964    }
965
966    return SubtitlesLayer;
967
968  })(TextLayer);
969
970  /**
971   * Transformation parameters
972   * Depends on 'util', 'transformation'
973   */
974  Param = (function() {
975
976    /**
977     * Represents a single parameter
978     * @class Param
979     * @param {string} name - The name of the parameter in snake_case
980     * @param {string} shortName - The name of the serialized form of the parameter.
981     *                         If a value is not provided, the parameter will not be serialized.
982     * @param {function} [process=cloudinary.Util.identity ] - Manipulate origValue when value is called
983     * @ignore
984     */
985    function Param(name, shortName, process) {
986      if (process == null) {
987        process = cloudinary.Util.identity;
988      }
989
990      /**
991       * The name of the parameter in snake_case
992       * @member {string} Param#name
993       */
994      this.name = name;
995
996      /**
997       * The name of the serialized form of the parameter
998       * @member {string} Param#shortName
999       */
1000      this.shortName = shortName;
1001
1002      /**
1003       * Manipulate origValue when value is called
1004       * @member {function} Param#process
1005       */
1006      this.process = process;
1007    }
1008
1009
1010    /**
1011     * Set a (unprocessed) value for this parameter
1012     * @function Param#set
1013     * @param {*} origValue - the value of the parameter
1014     * @return {Param} self for chaining
1015     */
1016
1017    Param.prototype.set = function(origValue) {
1018      this.origValue = origValue;
1019      return this;
1020    };
1021
1022
1023    /**
1024     * Generate the serialized form of the parameter
1025     * @function Param#serialize
1026     * @return {string} the serialized form of the parameter
1027     */
1028
1029    Param.prototype.serialize = function() {
1030      var val, valid;
1031      val = this.value();
1032      valid = cloudinary.Util.isArray(val) || cloudinary.Util.isPlainObject(val) || cloudinary.Util.isString(val) ? !cloudinary.Util.isEmpty(val) : val != null;
1033      if ((this.shortName != null) && valid) {
1034        return this.shortName + "_" + val;
1035      } else {
1036        return '';
1037      }
1038    };
1039
1040
1041    /**
1042     * Return the processed value of the parameter
1043     * @function Param#value
1044     */
1045
1046    Param.prototype.value = function() {
1047      return this.process(this.origValue);
1048    };
1049
1050    Param.norm_color = function(value) {
1051      return value != null ? value.replace(/^#/, 'rgb:') : void 0;
1052    };
1053
1054    Param.prototype.build_array = function(arg) {
1055      if (arg == null) {
1056        arg = [];
1057      }
1058      if (cloudinary.Util.isArray(arg)) {
1059        return arg;
1060      } else {
1061        return [arg];
1062      }
1063    };
1064
1065
1066    /**
1067    * Covert value to video codec string.
1068    *
1069    * If the parameter is an object,
1070    * @param {(string|Object)} param - the video codec as either a String or a Hash
1071    * @return {string} the video codec string in the format codec:profile:level
1072    * @example
1073    * vc_[ :profile : [level]]
1074    * or
1075      { codec: 'h264', profile: 'basic', level: '3.1' }
1076    * @ignore
1077     */
1078
1079    Param.process_video_params = function(param) {
1080      var video;
1081      switch (param.constructor) {
1082        case Object:
1083          video = "";
1084          if ('codec' in param) {
1085            video = param['codec'];
1086            if ('profile' in param) {
1087              video += ":" + param['profile'];
1088              if ('level' in param) {
1089                video += ":" + param['level'];
1090              }
1091            }
1092          }
1093          return video;
1094        case String:
1095          return param;
1096        default:
1097          return null;
1098      }
1099    };
1100
1101    return Param;
1102
1103  })();
1104  ArrayParam = (function(superClass) {
1105    extend(ArrayParam, superClass);
1106
1107
1108    /**
1109     * A parameter that represents an array
1110     * @param {string} name - The name of the parameter in snake_case
1111     * @param {string} shortName - The name of the serialized form of the parameter
1112     *                         If a value is not provided, the parameter will not be serialized.
1113     * @param {string} [sep='.'] - The separator to use when joining the array elements together
1114     * @param {function} [process=cloudinary.Util.identity ] - Manipulate origValue when value is called
1115     * @class ArrayParam
1116     * @extends Param
1117     * @ignore
1118     */
1119
1120    function ArrayParam(name, shortName, sep, process) {
1121      if (sep == null) {
1122        sep = '.';
1123      }
1124      this.sep = sep;
1125      ArrayParam.__super__.constructor.call(this, name, shortName, process);
1126    }
1127
1128    ArrayParam.prototype.serialize = function() {
1129      var arrayValue, flat, t;
1130      if (this.shortName != null) {
1131        arrayValue = this.value();
1132        if (cloudinary.Util.isEmpty(arrayValue)) {
1133          return '';
1134        } else if (cloudinary.Util.isString(arrayValue)) {
1135          return this.shortName + "_" + arrayValue;
1136        } else {
1137          flat = (function() {
1138            var j, len, results;
1139            results = [];
1140            for (j = 0, len = arrayValue.length; j < len; j++) {
1141              t = arrayValue[j];
1142              if (cloudinary.Util.isFunction(t.serialize)) {
1143                results.push(t.serialize());
1144              } else {
1145                results.push(t);
1146              }
1147            }
1148            return results;
1149          })();
1150          return this.shortName + "_" + (flat.join(this.sep));
1151        }
1152      } else {
1153        return '';
1154      }
1155    };
1156
1157    ArrayParam.prototype.value = function() {
1158      var j, len, ref, results, v;
1159      if (cloudinary.Util.isArray(this.origValue)) {
1160        ref = this.origValue;
1161        results = [];
1162        for (j = 0, len = ref.length; j < len; j++) {
1163          v = ref[j];
1164          results.push(this.process(v));
1165        }
1166        return results;
1167      } else {
1168        return this.process(this.origValue);
1169      }
1170    };
1171
1172    ArrayParam.prototype.set = function(origValue) {
1173      if ((origValue == null) || cloudinary.Util.isArray(origValue)) {
1174        return ArrayParam.__super__.set.call(this, origValue);
1175      } else {
1176        return ArrayParam.__super__.set.call(this, [origValue]);
1177      }
1178    };
1179
1180    return ArrayParam;
1181
1182  })(Param);
1183  TransformationParam = (function(superClass) {
1184    extend(TransformationParam, superClass);
1185
1186
1187    /**
1188     * A parameter that represents a transformation
1189     * @param {string} name - The name of the parameter in snake_case
1190     * @param {string} [shortName='t'] - The name of the serialized form of the parameter
1191     * @param {string} [sep='.'] - The separator to use when joining the array elements together
1192     * @param {function} [process=cloudinary.Util.identity ] - Manipulate origValue when value is called
1193     * @class TransformationParam
1194     * @extends Param
1195     * @ignore
1196     */
1197
1198    function TransformationParam(name, shortName, sep, process) {
1199      if (shortName == null) {
1200        shortName = "t";
1201      }
1202      if (sep == null) {
1203        sep = '.';
1204      }
1205      this.sep = sep;
1206      TransformationParam.__super__.constructor.call(this, name, shortName, process);
1207    }
1208
1209    TransformationParam.prototype.serialize = function() {
1210      var joined, result, t;
1211      if (cloudinary.Util.isEmpty(this.value())) {
1212        return '';
1213      } else if (cloudinary.Util.allStrings(this.value())) {
1214        joined = this.value().join(this.sep);
1215        if (!cloudinary.Util.isEmpty(joined)) {
1216          return this.shortName + "_" + joined;
1217        } else {
1218          return '';
1219        }
1220      } else {
1221        result = (function() {
1222          var j, len, ref, results;
1223          ref = this.value();
1224          results = [];
1225          for (j = 0, len = ref.length; j < len; j++) {
1226            t = ref[j];
1227            if (t != null) {
1228              if (cloudinary.Util.isString(t) && !cloudinary.Util.isEmpty(t)) {
1229                results.push(this.shortName + "_" + t);
1230              } else if (cloudinary.Util.isFunction(t.serialize)) {
1231                results.push(t.serialize());
1232              } else if (cloudinary.Util.isPlainObject(t) && !cloudinary.Util.isEmpty(t)) {
1233                results.push(new Transformation(t).serialize());
1234              } else {
1235                results.push(void 0);
1236              }
1237            }
1238          }
1239          return results;
1240        }).call(this);
1241        return cloudinary.Util.compact(result);
1242      }
1243    };
1244
1245    TransformationParam.prototype.set = function(origValue1) {
1246      this.origValue = origValue1;
1247      if (cloudinary.Util.isArray(this.origValue)) {
1248        return TransformationParam.__super__.set.call(this, this.origValue);
1249      } else {
1250        return TransformationParam.__super__.set.call(this, [this.origValue]);
1251      }
1252    };
1253
1254    return TransformationParam;
1255
1256  })(Param);
1257  RangeParam = (function(superClass) {
1258    extend(RangeParam, superClass);
1259
1260
1261    /**
1262     * A parameter that represents a range
1263     * @param {string} name - The name of the parameter in snake_case
1264     * @param {string} shortName - The name of the serialized form of the parameter
1265     *                         If a value is not provided, the parameter will not be serialized.
1266     * @param {function} [process=norm_range_value ] - Manipulate origValue when value is called
1267     * @class RangeParam
1268     * @extends Param
1269     * @ignore
1270     */
1271
1272    function RangeParam(name, shortName, process) {
1273      if (process == null) {
1274        process = this.norm_range_value;
1275      }
1276      RangeParam.__super__.constructor.call(this, name, shortName, process);
1277    }
1278
1279    RangeParam.norm_range_value = function(value) {
1280      var modifier, offset;
1281      offset = String(value).match(new RegExp('^' + offset_any_pattern + '$'));
1282      if (offset) {
1283        modifier = offset[5] != null ? 'p' : '';
1284        value = (offset[1] || offset[4]) + modifier;
1285      }
1286      return value;
1287    };
1288
1289    return RangeParam;
1290
1291  })(Param);
1292  RawParam = (function(superClass) {
1293    extend(RawParam, superClass);
1294
1295    function RawParam(name, shortName, process) {
1296      if (process == null) {
1297        process = cloudinary.Util.identity;
1298      }
1299      RawParam.__super__.constructor.call(this, name, shortName, process);
1300    }
1301
1302    RawParam.prototype.serialize = function() {
1303      return this.value();
1304    };
1305
1306    return RawParam;
1307
1308  })(Param);
1309  LayerParam = (function(superClass) {
1310    var LAYER_KEYWORD_PARAMS;
1311
1312    extend(LayerParam, superClass);
1313
1314    function LayerParam() {
1315      return LayerParam.__super__.constructor.apply(this, arguments);
1316    }
1317
1318    LayerParam.prototype.value = function() {
1319      var layerOptions, result;
1320      layerOptions = this.origValue;
1321      if (cloudinary.Util.isPlainObject(layerOptions)) {
1322        layerOptions = Util.withCamelCaseKeys(layerOptions);
1323        if (layerOptions.resourceType === "text" || (layerOptions.text != null)) {
1324          result = new cloudinary.TextLayer(layerOptions).toString();
1325        } else if (layerOptions.resourceType === "subtitles") {
1326          result = new cloudinary.SubtitlesLayer(layerOptions).toString();
1327        } else if (layerOptions.resourceType === "fetch" || (layerOptions.url != null)) {
1328          result = new cloudinary.FetchLayer(layerOptions).toString();
1329        } else {
1330          result = new cloudinary.Layer(layerOptions).toString();
1331        }
1332      } else if (/^fetch:.+/.test(layerOptions)) {
1333        result = new FetchLayer(layerOptions.substr(6)).toString();
1334      } else {
1335        result = layerOptions;
1336      }
1337      return result;
1338    };
1339
1340    LAYER_KEYWORD_PARAMS = [["font_weight", "normal"], ["font_style", "normal"], ["text_decoration", "none"], ["text_align", null], ["stroke", "none"], ["letter_spacing", null], ["line_spacing", null]];
1341
1342    LayerParam.prototype.textStyle = function(layer) {
1343      return (new cloudinary.TextLayer(layer)).textStyleIdentifier();
1344    };
1345
1346    return LayerParam;
1347
1348  })(Param);
1349  ExpressionParam = (function(superClass) {
1350    extend(ExpressionParam, superClass);
1351
1352    function ExpressionParam() {
1353      return ExpressionParam.__super__.constructor.apply(this, arguments);
1354    }
1355
1356    ExpressionParam.prototype.serialize = function() {
1357      return Expression.normalize(ExpressionParam.__super__.serialize.call(this));
1358    };
1359
1360    return ExpressionParam;
1361
1362  })(Param);
1363  parameters = {};
1364  parameters.Param = Param;
1365  parameters.ArrayParam = ArrayParam;
1366  parameters.RangeParam = RangeParam;
1367  parameters.RawParam = RawParam;
1368  parameters.TransformationParam = TransformationParam;
1369  parameters.LayerParam = LayerParam;
1370  parameters.ExpressionParam = ExpressionParam;
1371  Expression = (function() {
1372
1373    /**
1374     * @internal
1375     */
1376    var faceCount;
1377
1378    Expression.OPERATORS = {
1379      "=": 'eq',
1380      "!=": 'ne',
1381      "<": 'lt',
1382      ">": 'gt',
1383      "<=": 'lte',
1384      ">=": 'gte',
1385      "&&": 'and',
1386      "||": 'or',
1387      "*": "mul",
1388      "/": "div",
1389      "+": "add",
1390      "-": "sub"
1391    };
1392
1393
1394    /**
1395     * @internal
1396     */
1397
1398    Expression.PREDEFINED_VARS = {
1399      "aspect_ratio": "ar",
1400      "aspectRatio": "ar",
1401      "current_page": "cp",
1402      "currentPage": "cp",
1403      "face_count": "fc",
1404      "faceCount": "fc",
1405      "height": "h",
1406      "initial_aspect_ratio": "iar",
1407      "initial_height": "ih",
1408      "initial_width": "iw",
1409      "initialAspectRatio": "iar",
1410      "initialHeight": "ih",
1411      "initialWidth": "iw",
1412      "page_count": "pc",
1413      "page_x": "px",
1414      "page_y": "py",
1415      "pageCount": "pc",
1416      "pageX": "px",
1417      "pageY": "py",
1418      "tags": "tags",
1419      "width": "w"
1420    };
1421
1422
1423    /**
1424     * @internal
1425     */
1426
1427    Expression.BOUNDRY = "[ _]+";
1428
1429
1430    /**
1431     * Represents a transformation expression
1432     * @param {string} expressionStr - a expression in string format
1433     * @class Expression
1434     *
1435     */
1436
1437    function Expression(expressionStr) {
1438
1439      /**
1440        * @protected
1441        * @inner Expression-expressions
1442       */
1443      this.expressions = [];
1444      if (expressionStr != null) {
1445        this.expressions.push(Expression.normalize(expressionStr));
1446      }
1447    }
1448
1449
1450    /**
1451     * Convenience constructor method
1452     * @function Expression.new
1453     */
1454
1455    Expression["new"] = function(expressionStr) {
1456      return new this(expressionStr);
1457    };
1458
1459
1460    /**
1461     * Normalize a string expression
1462     * @function Cloudinary#normalize
1463     * @param {string} expression a expression, e.g. "w gt 100", "width_gt_100", "width > 100"
1464     * @return {string} the normalized form of the value expression, e.g. "w_gt_100"
1465     */
1466
1467    Expression.normalize = function(expression) {
1468      var operators, pattern, replaceRE;
1469      if (expression == null) {
1470        return expression;
1471      }
1472      expression = String(expression);
1473      operators = "\\|\\||>=|<=|&&|!=|>|=|<|/|-|\\+|\\*";
1474      pattern = "((" + operators + ")(?=[ _])|" + Object.keys(Expression.PREDEFINED_VARS).join("|") + ")";
1475      replaceRE = new RegExp(pattern, "g");
1476      expression = expression.replace(replaceRE, function(match) {
1477        return Expression.OPERATORS[match] || Expression.PREDEFINED_VARS[match];
1478      });
1479      return expression.replace(/[ _]+/g, '_');
1480    };
1481
1482
1483    /**
1484     * Serialize the expression
1485     * @return {string} the expression as a string
1486     */
1487
1488    Expression.prototype.serialize = function() {
1489      return Expression.normalize(this.expressions.join("_"));
1490    };
1491
1492    Expression.prototype.toString = function() {
1493      return this.serialize();
1494    };
1495
1496
1497    /**
1498     * Get the parent transformation of this expression
1499     * @return Transformation
1500     */
1501
1502    Expression.prototype.getParent = function() {
1503      return this.parent;
1504    };
1505
1506
1507    /**
1508     * Set the parent transformation of this expression
1509     * @param {Transformation} the parent transformation
1510     * @return {Expression} this expression
1511     */
1512
1513    Expression.prototype.setParent = function(parent) {
1514      this.parent = parent;
1515      return this;
1516    };
1517
1518
1519    /**
1520     * Add a expression
1521     * @function Expression#predicate
1522     * @internal
1523     */
1524
1525    Expression.prototype.predicate = function(name, operator, value) {
1526      if (Expression.OPERATORS[operator] != null) {
1527        operator = Expression.OPERATORS[operator];
1528      }
1529      this.expressions.push(name + "_" + operator + "_" + value);
1530      return this;
1531    };
1532
1533
1534    /**
1535     * @function Expression#and
1536     */
1537
1538    Expression.prototype.and = function() {
1539      this.expressions.push("and");
1540      return this;
1541    };
1542
1543
1544    /**
1545     * @function Expression#or
1546     */
1547
1548    Expression.prototype.or = function() {
1549      this.expressions.push("or");
1550      return this;
1551    };
1552
1553
1554    /**
1555     * Conclude expression
1556     * @function Expression#then
1557     * @return {Transformation} the transformation this expression is defined for
1558     */
1559
1560    Expression.prototype.then = function() {
1561      return this.getParent()["if"](this.toString());
1562    };
1563
1564
1565    /**
1566     * @function Expression#height
1567     * @param {string} operator the comparison operator (e.g. "<", "lt")
1568     * @param {string|number} value the right hand side value
1569     * @return {Expression} this expression
1570     */
1571
1572    Expression.prototype.height = function(operator, value) {
1573      return this.predicate("h", operator, value);
1574    };
1575
1576
1577    /**
1578     * @function Expression#width
1579     * @param {string} operator the comparison operator (e.g. "<", "lt")
1580     * @param {string|number} value the right hand side value
1581     * @return {Expression} this expression
1582     */
1583
1584    Expression.prototype.width = function(operator, value) {
1585      return this.predicate("w", operator, value);
1586    };
1587
1588
1589    /**
1590     * @function Expression#aspectRatio
1591     * @param {string} operator the comparison operator (e.g. "<", "lt")
1592     * @param {string|number} value the right hand side value
1593     * @return {Expression} this expression
1594     */
1595
1596    Expression.prototype.aspectRatio = function(operator, value) {
1597      return this.predicate("ar", operator, value);
1598    };
1599
1600
1601    /**
1602     * @function Expression#pages
1603     * @param {string} operator the comparison operator (e.g. "<", "lt")
1604     * @param {string|number} value the right hand side value
1605     * @return {Expression} this expression
1606     */
1607
1608    Expression.prototype.pageCount = function(operator, value) {
1609      return this.predicate("pc", operator, value);
1610    };
1611
1612
1613    /**
1614     * @function Expression#faces
1615     * @param {string} operator the comparison operator (e.g. "<", "lt")
1616     * @param {string|number} value the right hand side value
1617     * @return {Expression} this expression
1618     */
1619
1620    Expression.prototype.faceCount = function(operator, value) {
1621      return this.predicate("fc", operator, value);
1622    };
1623
1624    Expression.prototype.value = function(value) {
1625      this.expressions.push(value);
1626      return this;
1627    };
1628
1629
1630    /**
1631     */
1632
1633    Expression.variable = function(name, value) {
1634      return new this(name).value(value);
1635    };
1636
1637
1638    /**
1639      * @returns a new expression with the predefined variable "width"
1640      * @function Expression.width
1641     */
1642
1643    Expression.width = function() {
1644      return new this("width");
1645    };
1646
1647
1648    /**
1649      * @returns a new expression with the predefined variable "height"
1650      * @function Expression.height
1651     */
1652
1653    Expression.height = function() {
1654      return new this("height");
1655    };
1656
1657
1658    /**
1659      * @returns a new expression with the predefined variable "initialWidth"
1660      * @function Expression.initialWidth
1661     */
1662
1663    Expression.initialWidth = function() {
1664      return new this("initialWidth");
1665    };
1666
1667
1668    /**
1669      * @returns a new expression with the predefined variable "initialHeight"
1670      * @function Expression.initialHeight
1671     */
1672
1673    Expression.initialHeight = function() {
1674      return new this("initialHeight");
1675    };
1676
1677
1678    /**
1679      * @returns a new expression with the predefined variable "aspectRatio"
1680      * @function Expression.aspectRatio
1681     */
1682
1683    Expression.aspectRatio = function() {
1684      return new this("aspectRatio");
1685    };
1686
1687
1688    /**
1689      * @returns a new expression with the predefined variable "initialAspectRatio"
1690      * @function Expression.initialAspectRatio
1691     */
1692
1693    Expression.initialAspectRatio = function() {
1694      return new this("initialAspectRatio");
1695    };
1696
1697
1698    /**
1699      * @returns a new expression with the predefined variable "pageCount"
1700      * @function Expression.pageCount
1701     */
1702
1703    Expression.pageCount = function() {
1704      return new this("pageCount");
1705    };
1706
1707
1708    /**
1709      * @returns a new expression with the predefined variable "faceCount"
1710      * @function Expression.faceCount
1711     */
1712
1713    faceCount = function() {
1714      return new this("faceCount");
1715    };
1716
1717
1718    /**
1719      * @returns a new expression with the predefined variable "currentPage"
1720      * @function Expression.currentPage
1721     */
1722
1723    Expression.currentPage = function() {
1724      return new this("currentPage");
1725    };
1726
1727
1728    /**
1729      * @returns a new expression with the predefined variable "tags"
1730      * @function Expression.tags
1731     */
1732
1733    Expression.tags = function() {
1734      return new this("tags");
1735    };
1736
1737
1738    /**
1739      * @returns a new expression with the predefined variable "pageX"
1740      * @function Expression.pageX
1741     */
1742
1743    Expression.pageX = function() {
1744      return new this("pageX");
1745    };
1746
1747
1748    /**
1749      * @returns a new expression with the predefined variable "pageY"
1750      * @function Expression.pageY
1751     */
1752
1753    Expression.pageY = function() {
1754      return new this("pageY");
1755    };
1756
1757    return Expression;
1758
1759  })();
1760  Condition = (function(superClass) {
1761    extend(Condition, superClass);
1762
1763
1764    /**
1765     * Represents a transformation condition
1766     * @param {string} conditionStr - a condition in string format
1767     * @class Condition
1768     * @example
1769     * // normally this class is not instantiated directly
1770     * var tr = cloudinary.Transformation.new()
1771     *    .if().width( ">", 1000).and().aspectRatio("<", "3:4").then()
1772     *      .width(1000)
1773     *      .crop("scale")
1774     *    .else()
1775     *      .width(500)
1776     *      .crop("scale")
1777     *
1778     * var tr = cloudinary.Transformation.new()
1779     *    .if("w > 1000 and aspectRatio < 3:4")
1780     *      .width(1000)
1781     *      .crop("scale")
1782     *    .else()
1783     *      .width(500)
1784     *      .crop("scale")
1785     *
1786     */
1787
1788    function Condition(conditionStr) {
1789      Condition.__super__.constructor.call(this, conditionStr);
1790    }
1791
1792
1793    /**
1794     * @function Condition#height
1795     * @param {string} operator the comparison operator (e.g. "<", "lt")
1796     * @param {string|number} value the right hand side value
1797     * @return {Condition} this condition
1798     */
1799
1800    Condition.prototype.height = function(operator, value) {
1801      return this.predicate("h", operator, value);
1802    };
1803
1804
1805    /**
1806     * @function Condition#width
1807     * @param {string} operator the comparison operator (e.g. "<", "lt")
1808     * @param {string|number} value the right hand side value
1809     * @return {Condition} this condition
1810     */
1811
1812    Condition.prototype.width = function(operator, value) {
1813      return this.predicate("w", operator, value);
1814    };
1815
1816
1817    /**
1818     * @function Condition#aspectRatio
1819     * @param {string} operator the comparison operator (e.g. "<", "lt")
1820     * @param {string|number} value the right hand side value
1821     * @return {Condition} this condition
1822     */
1823
1824    Condition.prototype.aspectRatio = function(operator, value) {
1825      return this.predicate("ar", operator, value);
1826    };
1827
1828
1829    /**
1830     * @function Condition#pages
1831     * @param {string} operator the comparison operator (e.g. "<", "lt")
1832     * @param {string|number} value the right hand side value
1833     * @return {Condition} this condition
1834     */
1835
1836    Condition.prototype.pageCount = function(operator, value) {
1837      return this.predicate("pc", operator, value);
1838    };
1839
1840
1841    /**
1842     * @function Condition#faces
1843     * @param {string} operator the comparison operator (e.g. "<", "lt")
1844     * @param {string|number} value the right hand side value
1845     * @return {Condition} this condition
1846     */
1847
1848    Condition.prototype.faceCount = function(operator, value) {
1849      return this.predicate("fc", operator, value);
1850    };
1851
1852    return Condition;
1853
1854  })(Expression);
1855
1856  /**
1857   * Cloudinary configuration class
1858   * Depends on 'utils'
1859   */
1860  Configuration = (function() {
1861
1862    /**
1863     * Defaults configuration.
1864     */
1865    var DEFAULT_CONFIGURATION_PARAMS, ref;
1866
1867    DEFAULT_CONFIGURATION_PARAMS = {
1868      responsive_class: 'cld-responsive',
1869      responsive_use_breakpoints: true,
1870      round_dpr: true,
1871      secure: (typeof window !== "undefined" && window !== null ? (ref = window.location) != null ? ref.protocol : void 0 : void 0) === 'https:'
1872    };
1873
1874    Configuration.CONFIG_PARAMS = ["api_key", "api_secret", "callback", "cdn_subdomain", "cloud_name", "cname", "private_cdn", "protocol", "resource_type", "responsive", "responsive_class", "responsive_use_breakpoints", "responsive_width", "round_dpr", "secure", "secure_cdn_subdomain", "secure_distribution", "shorten", "type", "upload_preset", "url_suffix", "use_root_path", "version"];
1875
1876
1877    /**
1878     * Cloudinary configuration class
1879     * @constructor Configuration
1880     * @param {Object} options - configuration parameters
1881     */
1882
1883    function Configuration(options) {
1884      if (options == null) {
1885        options = {};
1886      }
1887      this.configuration = Util.cloneDeep(options);
1888      Util.defaults(this.configuration, DEFAULT_CONFIGURATION_PARAMS);
1889    }
1890
1891
1892    /**
1893     * Initialize the configuration.
1894     * The function first tries to retrieve the configuration form the environment and then from the document.
1895     * @function Configuration#init
1896     * @return {Configuration} returns this for chaining
1897     * @see fromDocument
1898     * @see fromEnvironment
1899     */
1900
1901    Configuration.prototype.init = function() {
1902      this.fromEnvironment();
1903      this.fromDocument();
1904      return this;
1905    };
1906
1907
1908    /**
1909     * Set a new configuration item
1910     * @function Configuration#set
1911     * @param {string} name - the name of the item to set
1912     * @param {*} value - the value to be set
1913     * @return {Configuration}
1914     *
1915     */
1916
1917    Configuration.prototype.set = function(name, value) {
1918      this.configuration[name] = value;
1919      return this;
1920    };
1921
1922
1923    /**
1924     * Get the value of a configuration item
1925     * @function Configuration#get
1926     * @param {string} name - the name of the item to set
1927     * @return {*} the configuration item
1928     */
1929
1930    Configuration.prototype.get = function(name) {
1931      return this.configuration[name];
1932    };
1933
1934    Configuration.prototype.merge = function(config) {
1935      if (config == null) {
1936        config = {};
1937      }
1938      Util.assign(this.configuration, Util.cloneDeep(config));
1939      return this;
1940    };
1941
1942
1943    /**
1944     * Initialize Cloudinary from HTML meta tags.
1945     * @function Configuration#fromDocument
1946     * @return {Configuration}
1947     * @example <meta name="cloudinary_cloud_name" content="mycloud">
1948     *
1949     */
1950
1951    Configuration.prototype.fromDocument = function() {
1952      var el, j, len, meta_elements;
1953      meta_elements = typeof document !== "undefined" && document !== null ? document.querySelectorAll('meta[name^="cloudinary_"]') : void 0;
1954      if (meta_elements) {
1955        for (j = 0, len = meta_elements.length; j < len; j++) {
1956          el = meta_elements[j];
1957          this.configuration[el.getAttribute('name').replace('cloudinary_', '')] = el.getAttribute('content');
1958        }
1959      }
1960      return this;
1961    };
1962
1963
1964    /**
1965     * Initialize Cloudinary from the `CLOUDINARY_URL` environment variable.
1966     *
1967     * This function will only run under Node.js environment.
1968     * @function Configuration#fromEnvironment
1969     * @requires Node.js
1970     */
1971
1972    Configuration.prototype.fromEnvironment = function() {
1973      var cloudinary_url, j, k, len, query, ref1, ref2, ref3, uri, uri
1973Regex, v, value;
1974      cloudinary_url = typeof process !== "undefined" && process !== null ? (ref1 = process.env) != null ? ref1.CLOUDINARY_URL : void 0 : void 0;
1975      if (cloudinary_url != null) {
1976        uriRegex = /cloudinary:\/\/(?:(\w+)(?:\:([\w-]+))?@)?([\w\.-]+)(?:\/([^?]*))?(?:\?(.+))?/;
1977        uri = uriRegex.exec(cloudinary_url);
1978        if (uri) {
1979          if (uri[3] != null) {
1980            this.configuration['cloud_name'] = uri[3];
1981          }
1982          if (uri[1] != null) {
1983            this.configuration['api_key'] = uri[1];
1984          }
1985          if (uri[2] != null) {
1986            this.configuration['api_secret'] = uri[2];
1987          }
1988          if (uri[4] != null) {
1989            this.configuration['private_cdn'] = uri[4] != null;
1990          }
1991          if (uri[4] != null) {
1992            this.configuration['secure_distribution'] = uri[4];
1993          }
1994          query = uri[5];
1995          if (query != null) {
1996            ref2 = query.split('&');
1997            for (j = 0, len = ref2.length; j < len; j++) {
1998              value = ref2[j];
1999              ref3 = value.split('='), k = ref3[0], v = ref3[1];
2000              if (v == null) {
2001                v = true;
2002              }
2003              this.configuration[k] = v;
2004            }
2005          }
2006        }
2007      }
2008      return this;
2009    };
2010
2011
2012    /**
2013     * Create or modify the Cloudinary client configuration
2014     *
2015     * Warning: `config()` returns the actual internal configuration object. modifying it will change the configuration.
2016     *
2017     * This is a backward compatibility method. For new code, use get(), merge() etc.
2018     * @function Configuration#config
2019     * @param {hash|string|boolean} new_config
2020     * @param {string} new_value
2021     * @returns {*} configuration, or value
2022     *
2023     * @see {@link fromEnvironment} for initialization using environment variables
2024     * @see {@link fromDocument} for initialization using HTML meta tags
2025     */
2026
2027    Configuration.prototype.config = function(new_config, new_value) {
2028      switch (false) {
2029        case new_value === void 0:
2030          this.set(new_config, new_value);
2031          return this.configuration;
2032        case !Util.isString(new_config):
2033          return this.get(new_config);
2034        case !Util.isPlainObject(new_config):
2035          this.merge(new_config);
2036          return this.configuration;
2037        default:
2038          return this.configuration;
2039      }
2040    };
2041
2042
2043    /**
2044     * Returns a copy of the configuration parameters
2045     * @function Configuration#toOptions
2046     * @returns {Object} a key:value collection of the configuration parameters
2047     */
2048
2049    Configuration.prototype.toOptions = function() {
2050      return Util.cloneDeep(this.configuration);
2051    };
2052
2053    return Configuration;
2054
2055  })();
2056
2057  /**
2058   * TransformationBase
2059   * Depends on 'configuration', 'parameters','util'
2060   * @internal
2061   */
2062  TransformationBase = (function() {
2063    var VAR_NAME_RE, lastArgCallback, processVar;
2064
2065    VAR_NAME_RE = /^\$[a-zA-Z0-9]+$/;
2066
2067    TransformationBase.prototype.trans_separator = '/';
2068
2069    TransformationBase.prototype.param_separator = ',';
2070
2071    lastArgCallback = function(args) {
2072      var callback;
2073      callback = args != null ? args[args.length - 1] : void 0;
2074      if (Util.isFunction(callback)) {
2075        return callback;
2076      } else {
2077        return void 0;
2078      }
2079    };
2080
2081
2082    /**
2083     * The base class for transformations.
2084     * Members of this class are documented as belonging to the {@link Transformation} class for convenience.
2085     * @class TransformationBase
2086     */
2087
2088    function TransformationBase(options) {
2089      var parent, trans;
2090      if (options == null) {
2091        options = {};
2092      }
2093
2094      /** @private */
2095      parent = void 0;
2096
2097      /** @private */
2098      trans = {};
2099
2100      /**
2101       * Return an options object that can be used to create an identical Transformation
2102       * @function Transformation#toOptions
2103       * @return {Object} Returns a plain object representing this transformation
2104       */
2105      this.toOptions || (this.toOptions = function(withChain) {
2106        var key, list, opt, ref, ref1, tr, value;
2107        if (withChain == null) {
2108          withChain = true;
2109        }
2110        opt = {};
2111        for (key in trans) {
2112          value = trans[key];
2113          opt[key] = value.origValue;
2114        }
2115        ref = this.otherOptions;
2116        for (key in ref) {
2117          value = ref[key];
2118          if (value !== void 0) {
2119            opt[key] = value;
2120          }
2121        }
2122        if (withChain && !Util.isEmpty(this.chained)) {
2123          list = (function() {
2124            var j, len, ref1, results;
2125            ref1 = this.chained;
2126            results = [];
2127            for (j = 0, len = ref1.length; j < len; j++) {
2128              tr = ref1[j];
2129              results.push(tr.toOptions());
2130            }
2131            return results;
2132          }).call(this);
2133          list.push(opt);
2134          opt = {};
2135          ref1 = this.otherOptions;
2136          for (key in ref1) {
2137            value = ref1[key];
2138            if (value !== void 0) {
2139              opt[key] = value;
2140            }
2141          }
2142          opt.transformation = list;
2143        }
2144        return opt;
2145      });
2146
2147      /**
2148       * Set a parent for this object for chaining purposes.
2149       *
2150       * @function Transformation#setParent
2151       * @param {Object} object - the parent to be assigned to
2152       * @returns {Transformation} Returns this instance for chaining purposes.
2153       */
2154      this.setParent || (this.setParent = function(object) {
2155        parent = object;
2156        if (object != null) {
2157          this.fromOptions(typeof object.toOptions === "function" ? object.toOptions() : void 0);
2158        }
2159        return this;
2160      });
2161
2162      /**
2163       * Returns the parent of this object in the chain
2164       * @function Transformation#getParent
2165       * @protected
2166       * @return {Object} Returns the parent of this object if there is any
2167       */
2168      this.getParent || (this.getParent = function() {
2169        return parent;
2170      });
2171
2172      /** @protected */
2173      this.param || (this.param = function(value, name, abbr, defaultValue, process) {
2174        if (process == null) {
2175          if (Util.isFunction(defaultValue)) {
2176            process = defaultValue;
2177          } else {
2178            process = Util.identity;
2179          }
2180        }
2181        trans[name] = new Param(name, abbr, process).set(value);
2182        return this;
2183      });
2184
2185      /** @protected */
2186      this.rawParam || (this.rawParam = function(value, name, abbr, defaultValue, process) {
2187        if (process == null) {
2188          process = Util.identity;
2189        }
2190        process = lastArgCallback(arguments);
2191        trans[name] = new RawParam(name, abbr, process).set(value);
2192        return this;
2193      });
2194
2195      /** @protected */
2196      this.rangeParam || (this.rangeParam = function(value, name, abbr, defaultValue, process) {
2197        if (process == null) {
2198          process = Util.identity;
2199        }
2200        process = lastArgCallback(arguments);
2201        trans[name] = new RangeParam(name, abbr, process).set(value);
2202        return this;
2203      });
2204
2205      /** @protected */
2206      this.arrayParam || (this.arrayParam = function(value, name, abbr, sep, defaultValue, process) {
2207        if (sep == null) {
2208          sep = ":";
2209        }
2210        if (defaultValue == null) {
2211          defaultValue = [];
2212        }
2213        if (process == null) {
2214          process = Util.identity;
2215        }
2216        process = lastArgCallback(arguments);
2217        trans[name] = new ArrayParam(name, abbr, sep, process).set(value);
2218        return this;
2219      });
2220
2221      /** @protected */
2222      this.transformationParam || (this.transformationParam = function(value, name, abbr, sep, defaultValue, process) {
2223        if (sep == null) {
2224          sep = ".";
2225        }
2226        if (process == null) {
2227          process = Util.identity;
2228        }
2229        process = lastArgCallback(arguments);
2230        trans[name] = new TransformationParam(name, abbr, sep, process).set(value);
2231        return this;
2232      });
2233      this.layerParam || (this.layerParam = function(value, name, abbr) {
2234        trans[name] = new LayerParam(name, abbr).set(value);
2235        return this;
2236      });
2237
2238      /**
2239       * Get the value associated with the given name.
2240       * @function Transformation#getValue
2241       * @param {string} name - the name of the parameter
2242       * @return {*} the processed value associated with the given name
2243       * @description Use {@link get}.origValue for the value originally provided for the parameter
2244       */
2245      this.getValue || (this.getValue = function(name) {
2246        var ref, ref1;
2247        return (ref = (ref1 = trans[name]) != null ? ref1.value() : void 0) != null ? ref : this.otherOptions[name];
2248      });
2249
2250      /**
2251       * Get the parameter object for the given parameter name
2252       * @function Transformation#get
2253       * @param {string} name the name of the transformation parameter
2254       * @returns {Param} the param object for the given name, or undefined
2255       */
2256      this.get || (this.get = function(name) {
2257        return trans[name];
2258      });
2259
2260      /**
2261       * Remove a transformation option from the transformation.
2262       * @function Transformation#remove
2263       * @param {string} name - the name of the option to remove
2264       * @return {*} Returns the option that was removed or null if no option by that name was found. The type of the
2265       *              returned value depends on the value.
2266       */
2267      this.remove || (this.remove = function(name) {
2268        var temp;
2269        switch (false) {
2270          case trans[name] == null:
2271            temp = trans[name];
2272            delete trans[name];
2273            return temp.origValue;
2274          case this.otherOptions[name] == null:
2275            temp = this.otherOptions[name];
2276            delete this.otherOptions[name];
2277            return temp;
2278          default:
2279            return null;
2280        }
2281      });
2282
2283      /**
2284       * Return an array of all the keys (option names) in the transformation.
2285       * @return {Array<string>} the keys in snakeCase format
2286       */
2287      this.keys || (this.keys = function() {
2288        var key;
2289        return ((function() {
2290          var results;
2291          results = [];
2292          for (key in trans) {
2293            if (key != null) {
2294              results.push(key.match(VAR_NAME_RE) ? key : Util.snakeCase(key));
2295            }
2296          }
2297          return results;
2298        })()).sort();
2299      });
2300
2301      /**
2302       * Returns a plain object representation of the transformation. Values are processed.
2303       * @function Transformation#toPlainObject
2304       * @return {Object} the transformation options as plain object
2305       */
2306      this.toPlainObject || (this.toPlainObject = function() {
2307        var hash, key, list, tr;
2308        hash = {};
2309        for (key in trans) {
2310          hash[key] = trans[key].value();
2311          if (Util.isPlainObject(hash[key])) {
2312            hash[key] = Util.cloneDeep(hash[key]);
2313          }
2314        }
2315        if (!Util.isEmpty(this.chained)) {
2316          list = (function() {
2317            var j, len, ref, results;
2318            ref = this.chained;
2319            results = [];
2320            for (j = 0, len = ref.length; j < len; j++) {
2321              tr = ref[j];
2322              results.push(tr.toPlainObject());
2323            }
2324            return results;
2325          }).call(this);
2326          list.push(hash);
2327          hash = {
2328            transformation: list
2329          };
2330        }
2331        return hash;
2332      });
2333
2334      /**
2335       * Complete the current transformation and chain to a new one.
2336       * In the URL, transformations are chained together by slashes.
2337       * @function Transformation#chain
2338       * @return {Transformation} Returns this transformation for chaining
2339       * @example
2340       * var tr = cloudinary.Transformation.new();
2341       * tr.width(10).crop('fit').chain().angle(15).serialize()
2342       * // produces "c_fit,w_10/a_15"
2343       */
2344      this.chain || (this.chain = function() {
2345        var names, tr;
2346        names = Object.getOwnPropertyNames(trans);
2347        if (names.length !== 0) {
2348          tr = new this.constructor(this.toOptions(false));
2349          this.resetTransformations();
2350          this.chained.push(tr);
2351        }
2352        return this;
2353      });
2354      this.resetTransformations || (this.resetTransformations = function() {
2355        trans = {};
2356        return this;
2357      });
2358      this.otherOptions || (this.otherOptions = {});
2359      this.chained = [];
2360      if (!Util.isEmpty(options)) {
2361        this.fromOptions(options);
2362      }
2363    }
2364
2365
2366    /**
2367     * Merge the provided options with own's options
2368     * @param {Object} [options={}] key-value list of options
2369     * @returns {Transformation} Returns this instance for chaining
2370     */
2371
2372    TransformationBase.prototype.fromOptions = function(options) {
2373      var key, opt;
2374      if (options instanceof TransformationBase) {
2375        this.fromTransformation(options);
2376      } else {
2377        options || (options = {});
2378        if (Util.isString(options) || Util.isArray(options)) {
2379          options = {
2380            transformation: options
2381          };
2382        }
2383        options = Util.cloneDeep(options, function(value) {
2384          if (value instanceof TransformationBase) {
2385            return new value.constructor(value.toOptions());
2386          }
2387        });
2388        if (options["if"]) {
2389          this.set("if", options["if"]);
2390          delete options["if"];
2391        }
2392        for (key in options) {
2393          opt = options[key];
2394          if (key.match(VAR_NAME_RE)) {
2395            if (key !== '$attr') {
2396              this.set('variable', key, opt);
2397            }
2398          } else {
2399            this.set(key, opt);
2400          }
2401        }
2402      }
2403      return this;
2404    };
2405
2406    TransformationBase.prototype.fromTransformation = function(other) {
2407      var j, key, len, ref;
2408      if (other instanceof TransformationBase) {
2409        ref = other.keys();
2410        for (j = 0, len = ref.length; j < len; j++) {
2411          key = ref[j];
2412          this.set(key, other.get(key).origValue);
2413        }
2414      }
2415      return this;
2416    };
2417
2418
2419    /**
2420     * Set a parameter.
2421     * The parameter name `key` is converted to
2422     * @param {string} key - the name of the parameter
2423     * @param {*} value - the value of the parameter
2424     * @returns {Transformation} Returns this instance for chaining
2425     */
2426
2427    TransformationBase.prototype.set = function() {
2428      var camelKey, key, values;
2429      key = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : [];
2430      camelKey = Util.camelCase(key);
2431      if (Util.contains(Transformation.methods, camelKey)) {
2432        this[camelKey].apply(this, values);
2433      } else {
2434        this.otherOptions[key] = values[0];
2435      }
2436      return this;
2437    };
2438
2439    TransformationBase.prototype.hasLayer = function() {
2440      return this.getValue("overlay") || this.getValue("underlay");
2441    };
2442
2443
2444    /**
2445     * Generate a string representation of the transformation.
2446     * @function Transformation#serialize
2447     * @return {string} Returns the transformation as a string
2448     */
2449
2450    TransformationBase.prototype.serialize = function() {
2451      var ifParam, j, len, paramList, ref, ref1, ref2, ref3, ref4, resultArray, t, tr, transformationList, transformationString, transformations, value, variables, vars;
2452      resultArray = (function() {
2453        var j, len, ref, results;
2454        ref = this.chained;
2455        results = [];
2456        for (j = 0, len = ref.length; j < len; j++) {
2457          tr = ref[j];
2458          results.push(tr.serialize());
2459        }
2460        return results;
2461      }).call(this);
2462      paramList = this.keys();
2463      transformations = (ref = this.get("transformation")) != null ? ref.serialize() : void 0;
2464      ifParam = (ref1 = this.get("if")) != null ? ref1.serialize() : void 0;
2465      variables = processVar((ref2 = this.get("variables")) != null ? ref2.value() : void 0);
2466      paramList = Util.difference(paramList, ["transformation", "if", "variables"]);
2467      vars = [];
2468      transformationList = [];
2469      for (j = 0, len = paramList.length; j < len; j++) {
2470        t = paramList[j];
2471        if (t.match(VAR_NAME_RE)) {
2472          vars.push(t + "_" + Expression.normalize((ref3 = this.get(t)) != null ? ref3.value() : void 0));
2473        } else {
2474          transformationList.push((ref4 = this.get(t)) != null ? ref4.serialize() : void 0);
2475        }
2476      }
2477      switch (false) {
2478        case !Util.isString(transformations):
2479          transformationList.push(transformations);
2480          break;
2481        case !Util.isArray(transformations):
2482          resultArray = resultArray.concat(transformations);
2483      }
2484      transformationList = (function() {
2485        var l, len1, results;
2486        results = [];
2487        for (l = 0, len1 = transformationList.length; l < len1; l++) {
2488          value = transformationList[l];
2489          if (Util.isArray(value) && !Util.isEmpty(value) || !Util.isArray(value) && value) {
2490            results.push(value);
2491          }
2492        }
2493        return results;
2494      })();
2495      transformationList = vars.sort().concat(variables).concat(transformationList.sort());
2496      if (ifParam === "if_end") {
2497        transformationList.push(ifParam);
2498      } else if (!Util.isEmpty(ifParam)) {
2499        transformationList.unshift(ifParam);
2500      }
2501      transformationString = Util.compact(transformationList).join(this.param_separator);
2502      if (!Util.isEmpty(transformationString)) {
2503        resultArray.push(transformationString);
2504      }
2505      return Util.compact(resultArray).join(this.trans_separator);
2506    };
2507
2508
2509    /**
2510     * Provide a list of all the valid transformation option names
2511     * @function Transformation#listNames
2512     * @private
2513     * @return {Array<string>} a array of all the valid option names
2514     */
2515
2516    TransformationBase.prototype.listNames = function() {
2517      return Transformation.methods;
2518    };
2519
2520
2521    /**
2522     * Returns attributes for an HTML tag.
2523     * @function Cloudinary.toHtmlAttributes
2524     * @return PlainObject
2525     */
2526
2527    TransformationBase.prototype.toHtmlAttributes = function() {
2528      var attrName, height, j, key, len, options, ref, ref1, ref2, ref3, value;
2529      options = {};
2530      ref = this.otherOptions;
2531      for (key in ref) {
2532        value = ref[key];
2533        if (!(!Util.contains(Transformation.PARAM_NAMES, Util.snakeCase(key)))) {
2534          continue;
2535        }
2536        attrName = /^html_/.test(key) ? key.slice(5) : key;
2537        options[attrName] = value;
2538      }
2539      ref1 = this.keys();
2540      for (j = 0, len = ref1.length; j < len; j++) {
2541        key = ref1[j];
2542        if (/^html_/.test(key)) {
2543          options[Util.camelCase(key.slice(5))] = this.getValue(key);
2544        }
2545      }
2546      if (!(this.hasLayer() || this.getValue("angle") || Util.contains(["fit", "limit", "lfill"], this.getValue("crop")))) {
2547        width = (ref2 = this.get("width")) != null ? ref2.origValue : void 0;
2548        height = (ref3 = this.get("height")) != null ? ref3.origValue : void 0;
2549        if (parseFloat(width) >= 1.0) {
2550          if (options['width'] == null) {
2551            options['width'] = width;
2552          }
2553        }
2554        if (parseFloat(height) >= 1.0) {
2555          if (options['height'] == null) {
2556            options['height'] = height;
2557          }
2558        }
2559      }
2560      return options;
2561    };
2562
2563    TransformationBase.prototype.isValidParamName = function(name) {
2564      return Transformation.methods.indexOf(Util.camelCase(name)) >= 0;
2565    };
2566
2567
2568    /**
2569     * Delegate to the parent (up the call chain) to produce HTML
2570     * @function Transformation#toHtml
2571     * @return {string} HTML representation of the parent if possible.
2572     * @example
2573     * tag = cloudinary.ImageTag.new("sample", {cloud_name: "demo"})
2574     * // ImageTag {name: "img", publicId: "sample"}
2575     * tag.toHtml()
2576     * // <img src="http://res.cloudinary.com/demo/image/upload/sample">
2577     * tag.transformation().crop("fit").width(300).toHtml()
2578     * // <img src="http://res.cloudinary.com/demo/image/upload/c_fit,w_300/sample">
2579     */
2580
2581    TransformationBase.prototype.toHtml = function() {
2582      var ref;
2583      return (ref = this.getParent()) != null ? typeof ref.toHtml === "function" ? ref.toHtml() : void 0 : void 0;
2584    };
2585
2586    TransformationBase.prototype.toString = function() {
2587      return this.serialize();
2588    };
2589
2590    processVar = function(varArray) {
2591      var j, len, name, ref, results, v;
2592      if (Util.isArray(varArray)) {
2593        results = [];
2594        for (j = 0, len = varArray.length; j < len; j++) {
2595          ref = varArray[j], name = ref[0], v = ref[1];
2596          results.push(name + "_" + (Expression.normalize(v)));
2597        }
2598        return results;
2599      } else {
2600        return varArray;
2601      }
2602
2603      /**
2604       * Transformation Class methods.
2605       * This is a list of the parameters defined in Transformation.
2606       * Values are camelCased.
2607       * @const Transformation.methods
2608       * @private
2609       * @ignore
2610       * @type {Array<string>}
2611       */
2612
2613      /**
2614       * Parameters that are filtered out before passing the options to an HTML tag.
2615       *
2616       * The list of parameters is a combination of `Transformation::methods` and `Configuration::CONFIG_PARAMS`
2617       * @const {Array<string>} Transformation.PARAM_NAMES
2618       * @private
2619       * @ignore
2620       * @see toHtmlAttributes
2621       */
2622    };
2623
2624    return TransformationBase;
2625
2626  })();
2627  Transformation = (function(superClass) {
2628    extend(Transformation, superClass);
2629
2630
2631    /**
2632     *  Represents a single transformation.
2633     *  @class Transformation
2634     *  @example
2635     *  t = new cloudinary.Transformation();
2636     * t.angle(20).crop("scale").width("auto");
2637     *
2638     * // or
2639     *
2640     * t = new cloudinary.Transformation( {angle: 20, crop: "scale", width: "auto"});
2641     */
2642
2643    function Transformation(options) {
2644      if (options == null) {
2645        options = {};
2646      }
2647      Transformation.__super__.constructor.call(this, options);
2648      this;
2649    }
2650
2651
2652    /**
2653     * Convenience constructor
2654     * @param {Object} options
2655     * @return {Transformation}
2656     * @example cl = cloudinary.Transformation.new( {angle: 20, crop: "scale", width: "auto"})
2657     */
2658
2659    Transformation["new"] = function(args) {
2660      return new Transformation(args);
2661    };
2662
2663
2664    /*
2665      Transformation Parameters
2666     */
2667
2668    Transformation.prototype.angle = function(value) {
2669      return this.arrayParam(value, "angle", "a", ".", Expression.normalize);
2670    };
2671
2672    Transformation.prototype.audioCodec = function(value) {
2673      return this.param(value, "audio_codec", "ac");
2674    };
2675
2676    Transformation.prototype.audioFrequency = function(value) {
2677      return this.param(value, "audio_frequency", "af");
2678    };
2679
2680    Transformation.prototype.aspectRatio = function(value) {
2681      return this.param(value, "aspect_ratio", "ar", Expression.normalize);
2682    };
2683
2684    Transformation.prototype.background = function(value) {
2685      return this.param(value, "background", "b", Param.norm_color);
2686    };
2687
2688    Transformation.prototype.bitRate = function(value) {
2689      return this.param(value, "bit_rate", "br");
2690    };
2691
2692    Transformation.prototype.border = function(value) {
2693      return this.param(value, "border", "bo", function(border) {
2694        if (Util.isPlainObject(border)) {
2695          border = Util.assign({}, {
2696            color: "black",
2697            width: 2
2698          }, border);
2699          return border.width + "px_solid_" + (Param.norm_color(border.color));
2700        } else {
2701          return border;
2702        }
2703      });
2704    };
2705
2706    Transformation.prototype.color = function(value) {
2707      return this.param(value, "color", "co", Param.norm_color);
2708    };
2709
2710    Transformation.prototype.colorSpace = function(value) {
2711      return this.param(value, "color_space", "cs");
2712    };
2713
2714    Transformation.prototype.crop = function(value) {
2715      return this.param(value, "crop", "c");
2716    };
2717
2718    Transformation.prototype.defaultImage = function(value) {
2719      return this.param(value, "default_image", "d");
2720    };
2721
2722    Transformation.prototype.delay = function(value) {
2723      return this.param(value, "delay", "dl");
2724    };
2725
2726    Transformation.prototype.density = function(value) {
2727      return this.param(value, "density", "dn");
2728    };
2729
2730    Transformation.prototype.duration = function(value) {
2731      return this.rangeParam(value, "duration", "du");
2732    };
2733
2734    Transformation.prototype.dpr = function(value) {
2735      return this.param(value, "dpr", "dpr", (function(_this) {
2736        return function(dpr) {
2737          dpr = dpr.toString();
2738          if (dpr != null ? dpr.match(/^\d+$/) : void 0) {
2739            return dpr + ".0";
2740          } else {
2741            return Expression.normalize(dpr);
2742          }
2743        };
2744      })(this));
2745    };
2746
2747    Transformation.prototype.effect = function(value) {
2748      return this.arrayParam(value, "effect", "e", ":", Expression.normalize);
2749    };
2750
2751    Transformation.prototype["else"] = function() {
2752      return this["if"]('else');
2753    };
2754
2755    Transformation.prototype.endIf = function() {
2756      return this["if"]('end');
2757    };
2758
2759    Transformation.prototype.endOffset = function(value) {
2760      return this.rangeParam(value, "end_offset", "eo");
2761    };
2762
2763    Transformation.prototype.fallbackContent = function(value) {
2764      return this.param(value, "fallback_content");
2765    };
2766
2767    Transformation.prototype.fetchFormat = function(value) {
2768      return this.param(value, "fetch_format", "f");
2769    };
2770
2771    Transformation.prototype.format = function(value) {
2772      return this.param(value, "format");
2773    };
2774
2775    Transformation.prototype.flags = function(value) {
2776      return this.arrayParam(value, "flags", "fl", ".");
2777    };
2778
2779    Transformation.prototype.gravity = function(value) {
2780      return this.param(value, "gravity", "g");
2781    };
2782
2783    Transformation.prototype.fps = function(value) {
2784      return this.param(value, "fps", "fps", (function(_this) {
2785        return function(fps) {
2786          if (Util.isString(fps)) {
2787            return fps;
2788          } else if (Util.isArray(fps)) {
2789            return fps.join("-");
2790          } else {
2791            return fps;
2792          }
2793        };
2794      })(this));
2795    };
2796
2797    Transformation.prototype.height = function(value) {
2798      return this.param(value, "height", "h", (function(_this) {
2799        return function() {
2800          if (_this.getValue("crop") || _this.getValue("overlay") || _this.getValue("underlay")) {
2801            return Expression.normalize(value);
2802          } else {
2803            return null;
2804          }
2805        };
2806      })(this));
2807    };
2808
2809    Transformation.prototype.htmlHeight = function(value) {
2810      return this.param(value, "html_height");
2811    };
2812
2813    Transformation.prototype.htmlWidth = function(value) {
2814      return this.param(value, "html_width");
2815    };
2816
2817    Transformation.prototype["if"] = function(value) {
2818      var i, ifVal, j, ref, trIf, trRest;
2819      if (value == null) {
2820        value = "";
2821      }
2822      switch (value) {
2823        case "else":
2824          this.chain();
2825          return this.param(value, "if", "if");
2826        case "end":
2827          this.chain();
2828          for (i = j = ref = this.chained.length - 1; j >= 0; i = j += -1) {
2829            ifVal = this.chained[i].getValue("if");
2830            if (ifVal === "end") {
2831              break;
2832            } else if (ifVal != null) {
2833              trIf = Transformation["new"]()["if"](ifVal);
2834              this.chained[i].remove("if");
2835              trRest = this.chained[i];
2836              this.chained[i] = Transformation["new"]().transformation([trIf, trRest]);
2837              if (ifVal !== "else") {
2838                break;
2839              }
2840            }
2841          }
2842          return this.param(value, "if", "if");
2843        case "":
2844          return Condition["new"]().setParent(this);
2845        default:
2846          return this.param(value, "if", "if", function(value) {
2847            return Condition["new"](value).toString();
2848          });
2849      }
2850    };
2851
2852    Transformation.prototype.keyframeInterval = function(value) {
2853      return this.param(value, "keyframe_interval", "ki");
2854    };
2855
2856    Transformation.prototype.offset = function(value) {
2857      var end_o, ref, start_o;
2858      ref = Util.isFunction(value != null ? value.split : void 0) ? value.split('..') 
2858: Util.isArray(value) ? value : [null, null], start_o = ref[0], end_o = ref[1];
2859      if (start_o != null) {
2860        this.startOffset(start_o);
2861      }
2862      if (end_o != null) {
2863        return this.endOffset(end_o);
2864      }
2865    };
2866
2867    Transformation.prototype.opacity = function(value) {
2868      return this.param(value, "opacity", "o", Expression.normalize);
2869    };
2870
2871    Transformation.prototype.overlay = function(value) {
2872      return this.layerParam(value, "overlay", "l");
2873    };
2874
2875    Transformation.prototype.page = function(value) {
2876      return this.param(value, "page", "pg");
2877    };
2878
2879    Transformation.prototype.poster = function(value) {
2880      return this.param(value, "poster");
2881    };
2882
2883    Transformation.prototype.prefix = function(value) {
2884      return this.param(value, "prefix", "p");
2885    };
2886
2887    Transformation.prototype.quality = function(value) {
2888      return this.param(value, "quality", "q", Expression.normalize);
2889    };
2890
2891    Transformation.prototype.radius = function(value) {
2892      return this.param(value, "radius", "r", Expression.normalize);
2893    };
2894
2895    Transformation.prototype.rawTransformation = function(value) {
2896      return this.rawParam(value, "raw_transformation");
2897    };
2898
2899    Transformation.prototype.size = function(value) {
2900      var height, ref;
2901      if (Util.isFunction(value != null ? value.split : void 0)) {
2902        ref = value.split('x'), width = ref[0], height = ref[1];
2903        this.width(width);
2904        return this.height(height);
2905      }
2906    };
2907
2908    Transformation.prototype.sourceTypes = function(value) {
2909      return this.param(value, "source_types");
2910    };
2911
2912    Transformation.prototype.sourceTransformation = function(value) {
2913      return this.param(value, "source_transformation");
2914    };
2915
2916    Transformation.prototype.startOffset = function(value) {
2917      return this.rangeParam(value, "start_offset", "so");
2918    };
2919
2920    Transformation.prototype.streamingProfile = function(value) {
2921      return this.param(value, "streaming_profile", "sp");
2922    };
2923
2924    Transformation.prototype.transformation = function(value) {
2925      return this.transformationParam(value, "transformation", "t");
2926    };
2927
2928    Transformation.prototype.underlay = function(value) {
2929      return this.layerParam(value, "underlay", "u");
2930    };
2931
2932    Transformation.prototype.variable = function(name, value) {
2933      return this.param(value, name, name);
2934    };
2935
2936    Transformation.prototype.variables = function(values) {
2937      return this.arrayParam(values, "variables");
2938    };
2939
2940    Transformation.prototype.videoCodec = function(value) {
2941      return this.param(value, "video_codec", "vc", Param.process_video_params);
2942    };
2943
2944    Transformation.prototype.videoSampling = function(value) {
2945      return this.param(value, "video_sampling", "vs");
2946    };
2947
2948    Transformation.prototype.width = function(value) {
2949      return this.param(value, "width", "w", (function(_this) {
2950        return function() {
2951          if (_this.getValue("crop") || _this.getValue("overlay") || _this.getValue("underlay")) {
2952            return Expression.normalize(value);
2953          } else {
2954            return null;
2955          }
2956        };
2957      })(this));
2958    };
2959
2960    Transformation.prototype.x = function(value) {
2961      return this.param(value, "x", "x", Expression.normalize);
2962    };
2963
2964    Transformation.prototype.y = function(value) {
2965      return this.param(value, "y", "y", Expression.normalize);
2966    };
2967
2968    Transformation.prototype.zoom = function(value) {
2969      return this.param(value, "zoom", "z", Expression.normalize);
2970    };
2971
2972    return Transformation;
2973
2974  })(TransformationBase);
2975
2976  /**
2977   * Transformation Class methods.
2978   * This is a list of the parameters defined in Transformation.
2979   * Values are camelCased.
2980   */
2981  Transformation.methods || (Transformation.methods = Util.difference(Util.functions(Transformation.prototype), Util.functions(TransformationBase.prototype)));
2982
2983  /**
2984   * Parameters that are filtered out before passing the options to an HTML tag.
2985   *
2986   * The list of parameters is a combination of `Transformation::methods` and `Configuration::CONFIG_PARAMS`
2987   */
2988  Transformation.PARAM_NAMES || (Transformation.PARAM_NAMES = ((function() {
2989    var j, len, ref, results;
2990    ref = Transformation.methods;
2991    results = [];
2992    for (j = 0, len = ref.length; j < len; j++) {
2993      m = ref[j];
2994      results.push(Util.snakeCase(m));
2995    }
2996    return results;
2997  })()).concat(Configuration.CONFIG_PARAMS));
2998
2999  /**
3000   * Generic HTML tag
3001   * Depends on 'transformation', 'util'
3002   */
3003  HtmlTag = (function() {
3004
3005    /**
3006     * Represents an HTML (DOM) tag
3007     * @constructor HtmlTag
3008     * @param {string} name - the name of the tag
3009     * @param {string} [publicId]
3010     * @param {Object} options
3011     * @example tag = new HtmlTag( 'div', { 'width': 10})
3012     */
3013    var toAttribute;
3014
3015    function HtmlTag(name, publicId, options) {
3016      var transformation;
3017      this.name = name;
3018      this.publicId = publicId;
3019      if (options == null) {
3020        if (Util.isPlainObject(publicId)) {
3021          options = publicId;
3022          this.publicId = void 0;
3023        } else {
3024          options = {};
3025        }
3026      }
3027      transformation = new Transformation(options);
3028      transformation.setParent(this);
3029      this.transformation = function() {
3030        return transformation;
3031      };
3032    }
3033
3034
3035    /**
3036     * Convenience constructor
3037     * Creates a new instance of an HTML (DOM) tag
3038     * @function HtmlTag.new
3039     * @param {string} name - the name of the tag
3040     * @param {string} [publicId]
3041     * @param {Object} options
3042     * @return {HtmlTag}
3043     * @example tag = HtmlTag.new( 'div', { 'width': 10})
3044     */
3045
3046    HtmlTag["new"] = function(name, publicId, options) {
3047      return new this(name, publicId, options);
3048    };
3049
3050
3051    /**
3052     * Represent the given key and value as an HTML attribute.
3053     * @function HtmlTag#toAttribute
3054     * @protected
3055     * @param {string} key - attribute name
3056     * @param {*|boolean} value - the value of the attribute. If the value is boolean `true`, return the key only.
3057     * @returns {string} the attribute  
3058     *
3059     */
3060
3061    toAttribute = function(key, value) {
3062      if (!value) {
3063        return void 0;
3064      } else if (value === true) {
3065        return key;
3066      } else {
3067        return key + "=\"" + value + "\"";
3068      }
3069    };
3070
3071
3072    /**
3073     * combine key and value from the `attr` to generate an HTML tag attributes string.
3074     * `Transformation::toHtmlTagOptions` is used to filter out transformation and configuration keys.
3075     * @protected
3076     * @param {Object} attrs
3077     * @return {string} the attributes in the format `'key1="value1" key2="value2"'`
3078     * @ignore
3079     */
3080
3081    HtmlTag.prototype.htmlAttrs = function(attrs) {
3082      var key, pairs, value;
3083      return pairs = ((function() {
3084        var results;
3085        results = [];
3086        for (key in attrs) {
3087          value = attrs[key];
3088          if (value) {
3089            results.push(toAttribute(key, value));
3090          }
3091        }
3092        return results;
3093      })()).sort().join(' ');
3094    };
3095
3096
3097    /**
3098     * Get all options related to this tag.
3099     * @function HtmlTag#getOptions
3100     * @returns {Object} the options
3101     *
3102     */
3103
3104    HtmlTag.prototype.getOptions = function() {
3105      return this.transformation().toOptions();
3106    };
3107
3108
3109    /**
3110     * Get the value of option `name`
3111     * @function HtmlTag#getOption
3112     * @param {string} name - the name of the option
3113     * @returns {*} Returns the value of the option
3114     *
3115     */
3116
3117    HtmlTag.prototype.getOption = function(name) {
3118      return this.transformation().getValue(name);
3119    };
3120
3121
3122    /**
3123     * Get the attributes of the tag.
3124     * @function HtmlTag#attributes
3125     * @returns {Object} attributes
3126     */
3127
3128    HtmlTag.prototype.attributes = function() {
3129      return this.transformation().toHtmlAttributes();
3130    };
3131
3132
3133    /**
3134     * Set a tag attribute named `name` to `value`
3135     * @function HtmlTag#setAttr
3136     * @param {string} name - the name of the attribute
3137     * @param {string} value - the value of the attribute
3138     */
3139
3140    HtmlTag.prototype.setAttr = function(name, value) {
3141      this.transformation().set("html_" + name, value);
3142      return this;
3143    };
3144
3145
3146    /**
3147     * Get the value of the tag attribute `name`
3148     * @function HtmlTag#getAttr
3149     * @param {string} name - the name of the attribute
3150     * @returns {*}
3151     */
3152
3153    HtmlTag.prototype.getAttr = function(name) {
3154      return this.attributes()["html_" + name] || this.attributes()[name];
3155    };
3156
3157
3158    /**
3159     * Remove the tag attributed named `name`
3160     * @function HtmlTag#removeAttr
3161     * @param {string} name - the name of the attribute
3162     * @returns {*}
3163     */
3164
3165    HtmlTag.prototype.removeAttr = function(name) {
3166      var ref;
3167      return (ref = this.transformation().remove("html_" + name)) != null ? ref : this.transformation().remove(name);
3168    };
3169
3170
3171    /**
3172     * @function HtmlTag#content
3173     * @protected
3174     * @ignore
3175     */
3176
3177    HtmlTag.prototype.content = function() {
3178      return "";
3179    };
3180
3181
3182    /**
3183     * @function HtmlTag#openTag
3184     * @protected
3185     * @ignore
3186     */
3187
3188    HtmlTag.prototype.openTag = function() {
3189      return "<" + this.name + " " + (this.htmlAttrs(this.attributes())) + ">";
3190    };
3191
3192
3193    /**
3194     * @function HtmlTag#closeTag
3195     * @protected
3196     * @ignore
3197     */
3198
3199    HtmlTag.prototype.closeTag = function() {
3200      return "</" + this.name + ">";
3201    };
3202
3203
3204    /**
3205     * Generates an HTML representation of the tag.
3206     * @function HtmlTag#toHtml
3207     * @returns {string} Returns HTML in string format
3208     */
3209
3210    HtmlTag.prototype.toHtml = function() {
3211      return this.openTag() + this.content() + this.closeTag();
3212    };
3213
3214
3215    /**
3216     * Creates a DOM object representing the tag.
3217     * @function HtmlTag#toDOM
3218     * @returns {Element}
3219     */
3220
3221    HtmlTag.prototype.toDOM = function() {
3222      var element, name, ref, value;
3223      if (!Util.isFunction(typeof document !== "undefined" && document !== null ? document.createElement : void 0)) {
3224        throw "Can't create DOM if document is not present!";
3225      }
3226      element = document.createElement(this.name);
3227      ref = this.attributes();
3228      for (name in ref) {
3229        value = ref[name];
3230        element[name] = value;
3231      }
3232      return element;
3233    };
3234
3235    HtmlTag.isResponsive = function(tag, responsiveClass) {
3236      var dataSrc;
3237      dataSrc = Util.getData(tag, 'src-cache') || Util.getData(tag, 'src');
3238      return Util.hasClass(tag, responsiveClass) && /\bw_auto\b/.exec(dataSrc);
3239    };
3240
3241    return HtmlTag;
3242
3243  })();
3244
3245  /**
3246   * Image Tag
3247   * Depends on 'tags/htmltag', 'cloudinary'
3248   */
3249  ImageTag = (function(superClass) {
3250    extend(ImageTag, superClass);
3251
3252
3253    /**
3254     * Creates an HTML (DOM) Image tag using Cloudinary as the source.
3255     * @constructor ImageTag
3256     * @extends HtmlTag
3257     * @param {string} [publicId]
3258     * @param {Object} [options]
3259     */
3260
3261    function ImageTag(publicId, options) {
3262      if (options == null) {
3263        options = {};
3264      }
3265      ImageTag.__super__.constructor.call(this, "img", publicId, options);
3266    }
3267
3268
3269    /** @override */
3270
3271    ImageTag.prototype.closeTag = function() {
3272      return "";
3273    };
3274
3275
3276    /** @override */
3277
3278    ImageTag.prototype.attributes = function() {
3279      var attr, options, srcAttribute;
3280      attr = ImageTag.__super__.attributes.call(this) || [];
3281      options = this.getOptions();
3282      srcAttribute = options.responsive && !options.client_hints ? 'data-src' : 'src';
3283      if (attr[srcAttribute] == null) {
3284        attr[srcAttribute] = new Cloudinary(this.getOptions()).url(this.publicId);
3285      }
3286      return attr;
3287    };
3288
3289    return ImageTag;
3290
3291  })(HtmlTag);
3292
3293  /**
3294   * Video Tag
3295   * Depends on 'tags/htmltag', 'util', 'cloudinary'
3296   */
3297  VideoTag = (function(superClass) {
3298    var DEFAULT_POSTER_OPTIONS, DEFAULT_VIDEO_SOURCE_TYPES, VIDEO_TAG_PARAMS;
3299
3300    extend(VideoTag, superClass);
3301
3302    VIDEO_TAG_PARAMS = ['source_types', 'source_transformation', 'fallback_content', 'poster'];
3303
3304    DEFAULT_VIDEO_SOURCE_TYPES = ['webm', 'mp4', 'ogv'];
3305
3306    DEFAULT_POSTER_OPTIONS = {
3307      format: 'jpg',
3308      resource_type: 'video'
3309    };
3310
3311
3312    /**
3313     * Creates an HTML (DOM) Video tag using Cloudinary as the source.
3314     * @constructor VideoTag
3315     * @extends HtmlTag
3316     * @param {string} [publicId]
3317     * @param {Object} [options]
3318     */
3319
3320    function VideoTag(publicId, options) {
3321      if (options == null) {
3322        options = {};
3323      }
3324      options = Util.defaults({}, options, Cloudinary.DEFAULT_VIDEO_PARAMS);
3325      VideoTag.__super__.constructor.call(this, "video", publicId.replace(/\.(mp4|ogv|webm)$/, ''), options);
3326    }
3327
3328
3329    /**
3330     * Set the transformation to apply on each source
3331     * @function VideoTag#setSourceTransformation
3332     * @param {Object} an object with pairs of source type and source transformation
3333     * @returns {VideoTag} Returns this instance for chaining purposes.
3334     */
3335
3336    VideoTag.prototype.setSourceTransformation = function(value) {
3337      this.transformation().sourceTransformation(value);
3338      return this;
3339    };
3340
3341
3342    /**
3343     * Set the source types to include in the video tag
3344     * @function VideoTag#setSourceTypes
3345     * @param {Array<string>} an array of source types
3346     * @returns {VideoTag} Returns this instance for chaining purposes.
3347     */
3348
3349    VideoTag.prototype.setSourceTypes = function(value) {
3350      this.transformation().sourceTypes(value);
3351      return this;
3352    };
3353
3354
3355    /**
3356     * Set the poster to be used in the video tag
3357     * @function VideoTag#setPoster
3358     * @param {string|Object} value
3359     * - string: a URL to use for the poster
3360     * - Object: transformation parameters to apply to the poster. May optionally include a public_id to use instead of the video public_id.
3361     * @returns {VideoTag} Returns this instance for chaining purposes.
3362     */
3363
3364    VideoTag.prototype.setPoster = function(value) {
3365      this.transformation().poster(value);
3366      return this;
3367    };
3368
3369
3370    /**
3371     * Set the content to use as fallback in the video tag
3372     * @function VideoTag#setFallbackContent
3373     * @param {string} value - the content to use, in HTML format
3374     * @returns {VideoTag} Returns this instance for chaining purposes.
3375     */
3376
3377    VideoTag.prototype.setFallbackContent = function(value) {
3378      this.transformation().fallbackContent(value);
3379      return this;
3380    };
3381
3382    VideoTag.prototype.content = function() {
3383      var cld, fallback, innerTags, mimeType, sourceTransformation, sourceTypes, src, srcType, transformation, videoType;
3384      sourceTypes = this.transformation().getValue('source_types');
3385      sourceTransformation = this.transformation().getValue('source_transformation');
3386      fallback = this.transformation().getValue('fallback_content');
3387      if (Util.isArray(sourceTypes)) {
3388        cld = new Cloudinary(this.getOptions());
3389        innerTags = (function() {
3390          var j, len, results;
3391          results = [];
3392          for (j = 0, len = sourceTypes.length; j < len; j++) {
3393            srcType = sourceTypes[j];
3394            transformation = sourceTransformation[srcType] || {};
3395            src = cld.url("" + this.publicId, Util.defaults({}, transformation, {
3396              resource_type: 'video',
3397              format: srcType
3398            }));
3399            videoType = srcType === 'ogv' ? 'ogg' : srcType;
3400            mimeType = 'video/' + videoType;
3401            results.push("<source " + (this.htmlAttrs({
3402              src: src,
3403              type: mimeType
3404            })) + ">");
3405          }
3406          return results;
3407        }).call(this);
3408      } else {
3409        innerTags = [];
3410      }
3411      return innerTags.join('') + fallback;
3412    };
3413
3414    VideoTag.prototype.attributes = function() {
3415      var a, attr, j, len, poster, ref, ref1, sourceTypes;
3416      sourceTypes = this.getOption('source_types');
3417      poster = (ref = this.getOption('poster')) != null ? ref : {};
3418      if (Util.isPlainObject(poster)) {
3419        defaults = poster.public_id != null ? Cloudinary.DEFAULT_IMAGE_PARAMS : DEFAULT_POSTER_OPTIONS;
3420        poster = new Cloudinary(this.getOptions()).url((ref1 = poster.public_id) != null ? ref1 : this.publicId, Util.defaults({}, poster, defaults));
3421      }
3422      attr = VideoTag.__super__.attributes.call(this) || [];
3423      for (j = 0, len = attr.length; j < len; j++) {
3424        a = attr[j];
3425        if (!Util.contains(VIDEO_TAG_PARAMS)) {
3426          attr = a;
3427        }
3428      }
3429      if (!Util.isArray(sourceTypes)) {
3430        attr["src"] = new Cloudinary(this.getOptions()).url(this.publicId, {
3431          resource_type: 'video',
3432          format: sourceTypes
3433        });
3434      }
3435      if (poster != null) {
3436        attr["poster"] = poster;
3437      }
3438      return attr;
3439    };
3440
3441    return VideoTag;
3442
3443  })(HtmlTag);
3444
3445  /**
3446   * Image Tag
3447   * Depends on 'tags/htmltag', 'cloudinary'
3448   */
3449  ClientHintsMetaTag = (function(superClass) {
3450    extend(ClientHintsMetaTag, superClass);
3451
3452
3453    /**
3454     * Creates an HTML (DOM) Meta tag that enables client-hints.
3455     * @constructor ClientHintsMetaTag
3456     * @extends HtmlTag
3457     */
3458
3459    function ClientHintsMetaTag(options) {
3460      ClientHintsMetaTag.__super__.constructor.call(this, 'meta', void 0, Util.assign({
3461        "http-equiv": "Accept-CH",
3462        content: "DPR, Viewport-Width, Width"
3463      }, options));
3464    }
3465
3466
3467    /** @override */
3468
3469    ClientHintsMetaTag.prototype.closeTag = function() {
3470      return "";
3471    };
3472
3473    return ClientHintsMetaTag;
3474
3475  })(HtmlTag);
3476  Cloudinary = (function() {
3477    var AKAMAI_SHARED_CDN, CF_SHARED_CDN, DEFAULT_POSTER_OPTIONS, DEFAULT_VIDEO_SOURCE_TYPES, OLD_AKAMAI_SHARED_CDN, SEO_TYPES, SHARED_CDN, VERSION, absolutize, applyBreakpoints, cdnSubdomainNumber, closestAbove, cloudinaryUrlPrefix, defaultBreakpoints, finalizeResourceType, findContainerWidth, maxWidth, updateDpr;
3478
3479    VERSION = "2.5.0";
3480
3481    CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net";
3482
3483    OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net";
3484
3485    AKAMAI_SHARED_CDN = "res.cloudinary.com";
3486
3487    SHARED_CDN = AKAMAI_SHARED_CDN;
3488
3489    DEFAULT_POSTER_OPTIONS = {
3490      format: 'jpg',
3491      resource_type: 'video'
3492    };
3493
3494    DEFAULT_VIDEO_SOURCE_TYPES = ['webm', 'mp4', 'ogv'];
3495
3496    SEO_TYPES = {
3497      "image/upload": "images",
3498      "image/private": "private_images",
3499      "image/authenticated": "authenticated_images",
3500      "raw/upload": "files",
3501      "video/upload": "videos"
3502    };
3503
3504
3505    /**
3506    * @const {Object} Cloudinary.DEFAULT_IMAGE_PARAMS
3507    * Defaults values for image parameters.
3508    *
3509    * (Previously defined using option_consume() )
3510     */
3511
3512    Cloudinary.DEFAULT_IMAGE_PARAMS = {
3513      resource_type: "image",
3514      transformation: [],
3515      type: 'upload'
3516    };
3517
3518
3519    /**
3520    * Defaults values for video parameters.
3521    * @const {Object} Cloudinary.DEFAULT_VIDEO_PARAMS
3522    * (Previously defined using option_consume() )
3523     */
3524
3525    Cloudinary.DEFAULT_VIDEO_PARAMS = {
3526      fallback_content: '',
3527      resource_type: "video",
3528      source_transformation: {},
3529      source_types: DEFAULT_VIDEO_SOURCE_TYPES,
3530      transformation: [],
3531      type: 'upload'
3532    };
3533
3534
3535    /**
3536     * Main Cloudinary class
3537     * @class Cloudinary
3538     * @param {Object} options - options to configure Cloudinary
3539     * @see Configuration for more details
3540     * @example
3541     *    var cl = new cloudinary.Cloudinary( { cloud_name: "mycloud"});
3542     *    var imgTag = cl.image("myPicID");
3543     */
3544
3545    function Cloudinary(options) {
3546      var configuration;
3547      this.devicePixelRatioCache = {};
3548      this.responsiveConfig = {};
3549      this.responsiveResizeInitialized = false;
3550      configuration = new Configuration(options);
3551      this.config = function(newConfig, newValue) {
3552        return configuration.config(newConfig, newValue);
3553      };
3554
3555      /**
3556       * Use \<meta\> tags in the document to configure this Cloudinary instance.
3557       * @return {Cloudinary} this for chaining
3558       */
3559      this.fromDocument = function() {
3560        configuration.fromDocument();
3561        return this;
3562      };
3563
3564      /**
3565       * Use environment variables to configure this Cloudinary instance.
3566       * @return {Cloudinary} this for chaining
3567       */
3568      this.fromEnvironment = function() {
3569        configuration.fromEnvironment();
3570        return this;
3571      };
3572
3573      /**
3574       * Initialize configuration.
3575       * @function Cloudinary#init
3576       * @see Configuration#init
3577       * @return {Cloudinary} this for chaining
3578       */
3579      this.init = function() {
3580        configuration.init();
3581        return this;
3582      };
3583    }
3584
3585
3586    /**
3587     * Convenience constructor
3588     * @param {Object} options
3589     * @return {Cloudinary}
3590     * @example cl = cloudinary.Cloudinary.new( { cloud_name: "mycloud"})
3591     */
3592
3593    Cloudinary["new"] = function(options) {
3594      return new this(options);
3595    };
3596
3597
3598    /**
3599     * Return the resource type and action type based on the given configuration
3600     * @function Cloudinary#finalizeResourceType
3601     * @param {Object|string} resourceType
3602     * @param {string} [type='upload']
3603     * @param {string} [urlSuffix]
3604     * @param {boolean} [useRootPath]
3605     * @param {boolean} [shorten]
3606     * @returns {string} resource_type/type
3607     * @ignore
3608     */
3609
3610    finalizeResourceType = function(resourceType, type, urlSuffix, useRootPath, shorten) {
3611      var key, options;
3612      if (resourceType == null) {
3613        resourceType = "image";
3614      }
3615      if (type == null) {
3616        type = "upload";
3617      }
3618      if (Util.isPlainObject(resourceType)) {
3619        options = resourceType;
3620        resourceType = options.resource_type;
3621        type = options.type;
3622        urlSuffix = options.url_suffix;
3623        useRootPath = options.use_root_path;
3624        shorten = options.shorten;
3625      }
3626      if (type == null) {
3627        type = 'upload';
3628      }
3629      if (urlSuffix != null) {
3630        resourceType = SEO_TYPES[resourceType + "/" + type];
3631        type = null;
3632        if (resourceType == null) {
3633          throw new Error("URL Suffix only supported for " + (((function() {
3634            var results;
3635            results = [];
3636            for (key in SEO_TYPES) {
3637              results.push(key);
3638            }
3639            return results;
3640          })()).join(', ')));
3641        }
3642      }
3643      if (useRootPath) {
3644        if (resourceType === 'image' && type === 'upload' || resourceType === "images") {
3645          resourceType = null;
3646          type = null;
3647        } else {
3648          throw new Error("Root path only supported for image/upload");
3649        }
3650      }
3651      if (shorten && resourceType === 'image' && type === 'upload') {
3652        resourceType = 'iu';
3653        type = null;
3654      }
3655      return [resourceType, type].join("/");
3656    };
3657
3658    absolutize = function(url) {
3659      var prefix;
3660      if (!url.match(/^https?:\//)) {
3661        prefix = document.location.protocol + '//' + document.location.host;
3662        if (url[0] === '?') {
3663          prefix += document.location.pathname;
3664        } else if (url[0] !== '/') {
3665          prefix += document.location.pathname.replace(/\/[^\/]*$/, '/');
3666        }
3667        url = prefix + url;
3668      }
3669      return url;
3670    };
3671
3672
3673    /**
3674     * Generate an resource URL.
3675     * @function Cloudinary#url
3676     * @param {string} publicId - the public ID of the resource
3677     * @param {Object} [options] - options for the tag and transformations, possible values include all {@link Transformation} parameters
3678     *                          and {@link Configuration} parameters
3679     * @param {string} [options.type='upload'] - the classification of the resource
3680     * @param {Object} [options.resource_type='image'] - the type of the resource
3681     * @return {string} The resource URL
3682     */
3683
3684    Cloudinary.prototype.url = function(publicId, options) {
3685      var error, error1, prefix, ref, resourceTypeAndType, transformation, transformationString, url, version;
3686      if (options == null) {
3687        options = {};
3688      }
3689      if (!publicId) {
3690        return publicId;
3691      }
3692      if (options instanceof Transformation) {
3693        options = options.toOptions();
3694      }
3695      options = Util.defaults({}, options, this.config(), Cloudinary.DEFAULT_IMAGE_PARAMS);
3696      if (options.type === 'fetch') {
3697        options.fetch_format = options.fetch_format || options.format;
3698        publicId = absolutize(publicId);
3699      }
3700      transformation = new Transformation(options);
3701      transformationString = transformation.serialize();
3702      if (!options.cloud_name) {
3703        throw 'Unknown cloud_name';
3704      }
3705      if (publicId.search('/') >= 0 && !publicId.match(/^v[0-9]+/) && !publicId.match(/^https?:\//) && !((ref = options.version) != null ? ref.toString() : void 0)) {
3706        options.version = 1;
3707      }
3708      if (publicId.match(/^https?:/)) {
3709        if (options.type === 'upload' || options.type === 'asset') {
3710          url = publicId;
3711        } else {
3712          publicId = encodeURIComponent(publicId).replace(/%3A/g, ':').replace(/%2F/g, '/');
3713        }
3714      } else {
3715        try {
3716          publicId = decodeURIComponent(publicId);
3717        } catch (error1) {
3718          error = error1;
3719        }
3720        publicId = encodeURIComponent(publicId).replace(/%3A/g, ':').replace(/%2F/g, '/');
3721        if (options.url_suffix) {
3722          if (options.url_suffix.match(/[\.\/]/)) {
3723            throw 'url_suffix should not include . or /';
3724          }
3725          publicId = publicId + '/' + options.url_suffix;
3726        }
3727        if (options.format) {
3728          if (!options.trust_public_id) {
3729            publicId = publicId.replace(/\.(jpg|png|gif|webp)$/, '');
3730          }
3731          publicId = publicId + '.' + options.format;
3732        }
3733      }
3734      prefix = cloudinaryUrlPrefix(publicId, options);
3735      resourceTypeAndType = finalizeResourceType(options.resource_type, options.type, options.url_suffix, options.use_root_path, options.shorten);
3736      version = options.version ? 'v' + options.version : '';
3737      return url || Util.compact([prefix, resourceTypeAndType, transformationString, version, publicId]).join('/').replace(/([^:])\/+/g, '$1/');
3738    };
3739
3740
3741    /**
3742     * Generate an video resource URL.
3743     * @function Cloudinary#video_url
3744     * @param {string} publicId - the public ID of the resource
3745     * @param {Object} [options] - options for the tag and transformations, possible values include all {@link Transformation} parameters
3746     *                          and {@link Configuration} parameters
3747     * @param {string} [options.type='upload'] - the classification of the resource
3748     * @return {string} The video URL
3749     */
3750
3751    Cloudinary.prototype.video_url = function(publicId, options) {
3752      options = Util.assign({
3753        resource_type: 'video'
3754      }, options);
3755      return this.url(publicId, options);
3756    };
3757
3758
3759    /**
3760     * Generate an video thumbnail URL.
3761     * @function Cloudinary#video_thumbnail_url
3762     * @param {string} publicId - the public ID of the resource
3763     * @param {Object} [options] - options for the tag and transformations, possible values include all {@link Transformation} parameters
3764     *                          and {@link Configuration} parameters
3765     * @param {string} [options.type='upload'] - the classification of the resource
3766     * @return {string} The video thumbnail URL
3767     */
3768
3769    Cloudinary.prototype.video_thumbnail_url = function(publicId, options) {
3770      options = Util.assign({}, DEFAULT_POSTER_OPTIONS, options);
3771      return this.url(publicId, options);
3772    };
3773
3774
3775    /**
3776     * Generate a string representation of the provided transformation options.
3777     * @function Cloudinary#transformation_string
3778     * @param {Object} options - the transformation options
3779     * @returns {string} The transformation string
3780     */
3781
3782    Cloudinary.prototype.transformation_string = function(options) {
3783      return new Transformation(options).serialize();
3784    };
3785
3786
3787    /**
3788     * Generate an image tag.
3789     * @function Cloudinary#image
3790     * @param {string} publicId - the public ID of the image
3791     * @param {Object} [options] - options for the tag and transformations
3792     * @return {HTMLImageElement} an image tag element
3793     */
3794
3795    Cloudinary.prototype.image = function(publicId, options) {
3796      var client_hints, img, ref, ref1;
3797      if (options == null) {
3798        options = {};
3799      }
3800      img = this.imageTag(publicId, options);
3801      client_hints = (ref = (ref1 = options.client_hints) != null ? ref1 : this.config('client_hints')) != null ? ref : false;
3802      if (!((options.src != null) || client_hints)) {
3803        img.setAttr("src", '');
3804      }
3805      img = img.toDOM();
3806      if (!client_hints) {
3807        Util.setData(img, 'src-cache', this.url(publicId, options));
3808        this.cloudinary_update(img, options);
3809      }
3810      return img;
3811    };
3812
3813
3814    /**
3815     * Creates a new ImageTag instance, configured using this own's configuration.
3816     * @function Cloudinary#imageTag
3817     * @param {string} publicId - the public ID of the resource
3818     * @param {Object} options - additional options to pass to the new ImageTag instance
3819     * @return {ImageTag} An ImageTag that is attached (chained) to this Cloudinary instance
3820     */
3821
3822    Cloudinary.prototype.imageTag = function(publicId, options) {
3823      var tag;
3824      tag = new ImageTag(publicId, this.config());
3825      tag.transformation().fromOptions(options);
3826      return tag;
3827    };
3828
3829
3830    /**
3831     * Generate an image tag for the video thumbnail.
3832     * @function Cloudinary#video_thumbnail
3833     * @param {string} publicId - the public ID of the video
3834     * @param {Object} [options] - options for the tag and transformations
3835     * @return {HTMLImageElement} An image tag element
3836     */
3837
3838    Cloudinary.prototype.video_thumbnail = function(publicId, options) {
3839      return this.image(publicId, Util.merge({}, DEFAULT_POSTER_OPTIONS, options));
3840    };
3841
3842
3843    /**
3844     * @function Cloudinary#facebook_profile_image
3845     * @param {string} publicId - the public ID of the image
3846     * @param {Object} [options] - options for the tag and transformations
3847     * @return {HTMLImageElement} an image tag element
3848     */
3849
3850    Cloudinary.prototype.facebook_profile_image = function(publicId, options) {
3851      return this.image(publicId, Util.assign({
3852        type: 'facebook'
3853      }, options));
3854    };
3855
3856
3857    /**
3858     * @function Cloudinary#twitter_profile_image
3859     * @param {string} publicId - the public ID of the image
3860     * @param {Object} [options] - options for the tag and transformations
3861     * @return {HTMLImageElement} an image tag element
3862     */
3863
3864    Cloudinary.prototype.twitter_profile_image = function(publicId, options) {
3865      return this.image(publicId, Util.assign({
3866        type: 'twitter'
3867      }, options));
3868    };
3869
3870
3871    /**
3872     * @function Cloudinary#twitter_name_profile_image
3873     * @param {string} publicId - the public ID of the image
3874     * @param {Object} [options] - options for the tag and transformations
3875     * @return {HTMLImageElement} an image tag element
3876     */
3877
3878    Cloudinary.prototype.twitter_name_profile_image = function(publicId, options) {
3879      return this.image(publicId, Util.assign({
3880        type: 'twitter_name'
3881      }, options));
3882    };
3883
3884
3885    /**
3886     * @function Cloudinary#gravatar_image
3887     * @param {string} publicId - the public ID of the image
3888     * @param {Object} [options] - options for the tag and transformations
3889     * @return {HTMLImageElement} an image tag element
3890     */
3891
3892    Cloudinary.prototype.gravatar_image = function(publicId, options) {
3893      return this.image(publicId, Util.assign({
3894        type: 'gravatar'
3895      }, options));
3896    };
3897
3898
3899    /**
3900     * @function Cloudinary#fetch_image
3901     * @param {string} publicId - the public ID of the image
3902     * @param {Object} [options] - options for the tag and transformations
3903     * @return {HTMLImageElement} an image tag element
3904     */
3905
3906    Cloudinary.prototype.fetch_image = function(publicId, options) {
3907      return this.image(publicId, Util.assign({
3908        type: 'fetch'
3909      }, options));
3910    };
3911
3912
3913    /**
3914     * @function Cloudinary#video
3915     * @param {string} publicId - the public ID of the image
3916     * @param {Object} [options] - options for the tag and transformations
3917     * @return {HTMLImageElement} an image tag element
3918     */
3919
3920    Cloudinary.prototype.video = function(publicId, options) {
3921      if (options == null) {
3922        options = {};
3923      }
3924      return this.videoTag(publicId, options).toHtml();
3925    };
3926
3927
3928    /**
3929     * Creates a new VideoTag instance, configured using this own's configuration.
3930     * @function Cloudinary#videoTag
3931     * @param {string} publicId - the public ID of the resource
3932     * @param {Object} options - additional options to pass to the new VideoTag instance
3933     * @return {VideoTag} A VideoTag that is attached (chained) to this Cloudinary instance
3934     */
3935
3936    Cloudinary.prototype.videoTag = function(publicId, options) {
3937      options = Util.defaults({}, options, this.config());
3938      return new VideoTag(publicId, options);
3939    };
3940
3941
3942    /**
3943     * Generate the URL of the sprite image
3944     * @function Cloudinary#sprite_css
3945     * @param {string} publicId - the public ID of the resource
3946     * @param {Object} [options] - options for the tag and transformations
3947     * @see {@link http://cloudinary.com/documentation/sprite_generation Sprite generation}
3948     */
3949
3950    Cloudinary.prototype.sprite_css = function(publicId, options) {
3951      options = Util.assign({
3952        type: 'sprite'
3953      }, options);
3954      if (!publicId.match(/.css$/)) {
3955        options.format = 'css';
3956      }
3957      return this.url(publicId, options);
3958    };
3959
3960
3961    /**
3962    * Initialize the responsive behaviour.<br>
3963    * Calls {@link Cloudinary#cloudinary_update} to modify image tags.
3964     * @function Cloudinary#responsive
3965    * @param {Object} options
3966    * @param {String} [options.responsive_class='cld-responsive'] - provide an alternative class used to locate img tags
3967    * @param {number} [options.responsive_debounce=100] - the debounce interval in milliseconds.
3968    * @param {boolean} [bootstrap=true] if true processes the img tags by calling cloudinary_update. When false the tags will be processed only after a resize event.
3969    * @see {@link Cloudinary#cloudinary_update} for additional configuration parameters
3970     */
3971
3972    Cloudinary.prototype.responsive = function(options, bootstrap) {
3973      var ref, ref1, ref2, responsiveClass, responsiveResize, timeout;
3974      if (bootstrap == null) {
3975        bootstrap = true;
3976      }
3977      this.responsiveConfig = Util.merge(this.responsiveConfig || {}, options);
3978      responsiveClass = (ref = this.responsiveConfig['responsive_class']) != null ? ref : this.config('responsive_class');
3979      if (bootstrap) {
3980        this.cloudinary_update("img." + responsiveClass + ", img.cld-hidpi", this.responsiveConfig);
3981      }
3982      responsiveResize = (ref1 = (ref2 = this.responsiveConfig['responsive_resize']) != null ? ref2 : this.config('responsive_resize')) != null ? ref1 : true;
3983      if (responsiveResize && !this.responsiveResizeInitialized) {
3984        this.responsiveConfig.resizing = this.responsiveResizeInitialized = true;
3985        timeout = null;
3986        return window.addEventListener('resize', (function(_this) {
3987          return function() {
3988            var debounce, ref3, ref4, reset, run, wait, waitFunc;
3989            debounce = (ref3 = (ref4 = _this.responsiveConfig['responsive_debounce']) != null ? ref4 : _this.config('responsive_debounce')) != null ? ref3 : 100;
3990            reset = function() {
3991              if (timeout) {
3992                clearTimeout(timeout);
3993                return timeout = null;
3994              }
3995            };
3996            run = function() {
3997              return _this.cloudinary_update("img." + responsiveClass, _this.responsiveConfig);
3998            };
3999            waitFunc = function() {
4000              reset();
4001              return run();
4002            };
4003            wait = function() {
4004              reset();
4005              return timeout = setTimeout(waitFunc, debounce);
4006            };
4007            if (debounce) {
4008              return wait();
4009            } else {
4010              return run();
4011            }
4012          };
4013        })(this));
4014      }
4015    };
4016
4017
4018    /**
4019     * @function Cloudinary#calc_breakpoint
4020     * @private
4021     * @ignore
4022     */
4023
4024    Cloudinary.prototype.calc_breakpoint = function(element, width, steps) {
4025      var breakpoints, point;
4026      breakpoints = Util.getData(element, 'breakpoints') || Util.getData(element, 'stoppoints') || this.config('breakpoints') || this.config('stoppoints') || defaultBreakpoints;
4027      if (Util.isFunction(breakpoints)) {
4028        return breakpoints(width, steps);
4029      } else {
4030        if (Util.isString(breakpoints)) {
4031          breakpoints = ((function() {
4032            var j, len, ref, results;
4033            ref = breakpoints.split(',');
4034            results = [];
4035            for (j = 0, len = ref.length; j < len; j++) {
4036              point = ref[j];
4037              results.push(parseInt(point));
4038            }
4039            return results;
4040          })()).sort(function(a, b) {
4041            return a - b;
4042          });
4043        }
4044        return closestAbove(breakpoints, width);
4045      }
4046    };
4047
4048
4049    /**
4050     * @function Cloudinary#calc_stoppoint
4051     * @deprecated Use {@link calc_breakpoint} instead.
4052     * @private
4053     * @ignore
4054     */
4055
4056    Cloudinary.prototype.calc_stoppoint = Cloudinary.prototype.calc_breakpoint;
4057
4058
4059    /**
4060     * @function Cloudinary#device_pixel_ratio
4061     * @private
4062     */
4063
4064    Cloudinary.prototype.device_pixel_ratio = function(roundDpr) {
4065      var dpr, dprString;
4066      if (roundDpr == null) {
4067        roundDpr = true;
4068      }
4069      dpr = (typeof window !== "undefined" && window !== null ? window.devicePixelRatio : void 0) || 1;
4070      if (roundDpr) {
4071        dpr = Math.ceil(dpr);
4072      }
4073      if (dpr <= 0 || dpr === NaN) {
4074        dpr = 1;
4075      }
4076      dprString = dpr.toString();
4077      if (dprString.match(/^\d+$/)) {
4078        dprString += '.0';
4079      }
4080      return dprString;
4081    };
4082
4083    defaultBreakpoints = function(width, steps) {
4084      if (steps == null) {
4085        steps = 100;
4086      }
4087      return steps * Math.ceil(width / steps);
4088    };
4089
4090    closestAbove = function(list, value) {
4091      var i;
4092      i = list.length - 2;
4093      while (i >= 0 && list[i] >= value) {
4094        i--;
4095      }
4096      return list[i + 1];
4097    };
4098
4099    cdnSubdomainNumber = function(publicId) {
4100      return crc32(publicId) % 5 + 1;
4101    };
4102
4103    cloudinaryUrlPrefix = function(publicId, options) {
4104      var cdnPart, host, path, protocol, ref, subdomain;
4105      if (((ref = options.cloud_name) != null ? ref.indexOf("/") : void 0) === 0) {
4106        return '/res' + options.cloud_name;
4107      }
4108      protocol = "http://";
4109      cdnPart = "";
4110      subdomain = "res";
4111      host = ".cloudinary.com";
4112      path = "/" + options.cloud_name;
4113      if (options.protocol) {
4114        protocol = options.protocol + '//';
4115      }
4116      if (options.private_cdn) {
4117        cdnPart = options.cloud_name + "-";
4118        path = "";
4119      }
4120      if (options.cdn_subdomain) {
4121        subdomain = "res-" + cdnSubdomainNumber(publicId);
4122      }
4123      if (options.secure) {
4124        protocol = "https://";
4125        if (options.secure_cdn_subdomain === false) {
4126          subdomain = "res";
4127        }
4128        if ((options.secure_distribution != null) && options.secure_distribution !== OLD_AKAMAI_SHARED_CDN && options.secure_distribution !== SHARED_CDN) {
4129          cdnPart = "";
4130          subdomain = "";
4131          host = options.secure_distribution;
4132        }
4133      } else if (options.cname) {
4134        protocol = "http://";
4135        cdnPart = "";
4136        subdomain = options.cdn_subdomain ? 'a' + ((crc32(publicId) % 5) + 1) + '.' : '';
4137        host = options.cname;
4138      }
4139      return [protocol, cdnPart, subdomain, host, path].join("");
4140    };
4141
4142
4143    /**
4144    * Finds all `img` tags under each node and sets it up to provide the image through Cloudinary
4145    * @param {Element[]} nodes the parent nodes to search for img under
4146    * @param {Object} [options={}] options and transformations params
4147    * @function Cloudinary#processImageTags
4148     */
4149
4150    Cloudinary.prototype.processImageTags = function(nodes, options) {
4151      var images, imgOptions, node, publicId, url;
4152      if (options == null) {
4153        options = {};
4154      }
4155      if (Util.isEmpty(nodes)) {
4156        return this;
4157      }
4158      options = Util.defaults({}, options, this.config());
4159      images = (function() {
4160        var j, len, ref, results;
4161        results = [];
4162        for (j = 0, len = nodes.length; j < len; j++) {
4163          node = nodes[j];
4164          if (!(((ref = node.tagName) != null ? ref.toUpperCase() : void 0) === 'IMG')) {
4165            continue;
4166          }
4167          imgOptions = Util.assign({
4168            width: node.getAttribute('width'),
4169            height: node.getAttribute('height'),
4170            src: node.getAttribute('src')
4171          }, options);
4172          publicId = imgOptions['source'] || imgOptions['src'];
4173          delete imgOptions['source'];
4174          delete imgOptions['src'];
4175          url = this.url(publicId, imgOptions);
4176          imgOptions = new Transformation(imgOptions).toHtmlAttributes();
4177          Util.setData(node, 'src-cache', url);
4178          node.setAttribute('width', imgOptions.width);
4179          node.setAttribute('height', imgOptions.height);
4180          results.push(node);
4181        }
4182        return results;
4183      }).call(this);
4184      this.cloudinary_update(images, options);
4185      return this;
4186    };
4187
4188    applyBreakpoints = function(tag, width, steps, options) {
4189      var ref, ref1, ref2, responsive_use_breakpoints;
4190      responsive_use_breakpoints = (ref = (ref1 = (ref2 = options['responsive_use_breakpoints']) != null ? ref2 : options['responsive_use_stoppoints']) != null ? ref1 : this.config('responsive_use_breakpoints')) != null ? ref : this.config('responsive_use_stoppoints');
4191      if ((!responsive_use_breakpoints) || (responsive_use_breakpoints === 'resize' && !
4191options.resizing)) {
4192        return width;
4193      } else {
4194        return this.calc_breakpoint(tag, width, steps);
4195      }
4196    };
4197
4198    findContainerWidth = function(element) {
4199      var containerWidth, style;
4200      containerWidth = 0;
4201      while (((element = element != null ? element.parentNode : void 0) instanceof Element) && !containerWidth) {
4202        style = window.getComputedStyle(element);
4203        if (!/^inline/.test(style.display)) {
4204          containerWidth = Util.width(element);
4205        }
4206      }
4207      return containerWidth;
4208    };
4209
4210    updateDpr = function(dataSrc, roundDpr) {
4211      return dataSrc.replace(/\bdpr_(1\.0|auto)\b/g, 'dpr_' + this.device_pixel_ratio(roundDpr));
4212    };
4213
4214    maxWidth = function(requiredWidth, tag) {
4215      var imageWidth;
4216      imageWidth = Util.getData(tag, 'width') || 0;
4217      if (requiredWidth > imageWidth) {
4218        imageWidth = requiredWidth;
4219        Util.setData(tag, 'width', requiredWidth);
4220      }
4221      return imageWidth;
4222    };
4223
4224
4225    /**
4226    * Update hidpi (dpr_auto) and responsive (w_auto) fields according to the current container size and the device pixel ratio.
4227    * Only images marked with the cld-responsive class have w_auto updated.
4228    * @function Cloudinary#cloudinary_update
4229    * @param {(Array|string|NodeList)} elements - the elements to modify
4230    * @param {Object} options
4231    * @param {boolean|string} [options.responsive_use_breakpoints=true]
4232    *  - when `true`, always use breakpoints for width
4233    * - when `"resize"` use exact width on first render and breakpoints on resize
4234    * - when `false` always use exact width
4235    * @param {boolean} [options.responsive] - if `true`, enable responsive on this element. Can be done by adding cld-responsive.
4236    * @param {boolean} [options.responsive_preserve_height] - if set to true, original css height is preserved.
4237    *   Should only be used if the transformation supports different aspect ratios.
4238     */
4239
4240    Cloudinary.prototype.cloudinary_update = function(elements, options) {
4241      var containerWidth, dataSrc, j, len, match, ref, ref1, ref2, ref3, ref4, ref5, requiredWidth, responsive, responsiveClass, roundDpr, setUrl, tag;
4242      if (options == null) {
4243        options = {};
4244      }
4245      if (elements === null) {
4246        return this;
4247      }
4248      responsive = (ref = (ref1 = options.responsive) != null ? ref1 : this.config('responsive')) != null ? ref : false;
4249      elements = (function() {
4250        switch (false) {
4251          case !Util.isArray(elements):
4252            return elements;
4253          case elements.constructor.name !== "NodeList":
4254            return elements;
4255          case !Util.isString(elements):
4256            return document.querySelectorAll(elements);
4257          default:
4258            return [elements];
4259        }
4260      })();
4261      responsiveClass = (ref2 = (ref3 = this.responsiveConfig['responsive_class']) != null ? ref3 : options['responsive_class']) != null ? ref2 : this.config('responsive_class');
4262      roundDpr = (ref4 = options['round_dpr']) != null ? ref4 : this.config('round_dpr');
4263      for (j = 0, len = elements.length; j < len; j++) {
4264        tag = elements[j];
4265        if (!((ref5 = tag.tagName) != null ? ref5.match(/img/i) : void 0)) {
4266          continue;
4267        }
4268        setUrl = true;
4269        if (responsive) {
4270          Util.addClass(tag, responsiveClass);
4271        }
4272        dataSrc = Util.getData(tag, 'src-cache') || Util.getData(tag, 'src');
4273        if (!Util.isEmpty(dataSrc)) {
4274          dataSrc = updateDpr.call(this, dataSrc, roundDpr);
4275          if (HtmlTag.isResponsive(tag, responsiveClass)) {
4276            containerWidth = findContainerWidth(tag);
4277            if (containerWidth !== 0) {
4278              switch (false) {
4279                case !/w_auto:breakpoints/.test(dataSrc):
4280                  requiredWidth = maxWidth(containerWidth, tag);
4281                  dataSrc = dataSrc.replace(/w_auto:breakpoints([_0-9]*)(:[0-9]+)?/, "w_auto:breakpoints$1:" + requiredWidth);
4282                  break;
4283                case !(match = /w_auto(:(\d+))?/.exec(dataSrc)):
4284                  requiredWidth = applyBreakpoints.call(this, tag, containerWidth, match[2], options);
4285                  requiredWidth = maxWidth(requiredWidth, tag);
4286                  dataSrc = dataSrc.replace(/w_auto[^,\/]*/g, "w_" + requiredWidth);
4287              }
4288              Util.removeAttribute(tag, 'width');
4289              if (!options.responsive_preserve_height) {
4290                Util.removeAttribute(tag, 'height');
4291              }
4292            } else {
4293              setUrl = false;
4294            }
4295          }
4296          if (setUrl) {
4297            Util.setAttribute(tag, 'src', dataSrc);
4298          }
4299        }
4300      }
4301      return this;
4302    };
4303
4304
4305    /**
4306     * Provide a transformation object, initialized with own's options, for chaining purposes.
4307     * @function Cloudinary#transformation
4308     * @param {Object} options
4309     * @return {Transformation}
4310     */
4311
4312    Cloudinary.prototype.transformation = function(options) {
4313      return Transformation["new"](this.config()).fromOptions(options).setParent(this);
4314    };
4315
4316    return Cloudinary;
4317
4318  })();
4319
4320  /**
4321   * Cloudinary jQuery plugin
4322   * Depends on 'jquery', 'util', 'transformation', 'cloudinary'
4323   */
4324  CloudinaryJQuery = (function(superClass) {
4325    extend(CloudinaryJQuery, superClass);
4326
4327
4328    /**
4329     * Cloudinary class with jQuery support
4330     * @constructor CloudinaryJQuery
4331     * @extends Cloudinary
4332     */
4333
4334    function CloudinaryJQuery(options) {
4335      CloudinaryJQuery.__super__.constructor.call(this, options);
4336    }
4337
4338
4339    /**
4340     * @override
4341     */
4342
4343    CloudinaryJQuery.prototype.image = function(publicId, options) {
4344      var client_hints, img, ref, ref1;
4345      if (options == null) {
4346        options = {};
4347      }
4348      img = this.imageTag(publicId, options);
4349      client_hints = (ref = (ref1 = options.client_hints) != null ? ref1 : this.config('client_hints')) != null ? ref : false;
4350      if (!((options.src != null) || client_hints)) {
4351        img.setAttr("src", '');
4352      }
4353      img = jQuery(img.toHtml());
4354      if (!client_hints) {
4355        img.data('src-cache', this.url(publicId, options)).cloudinary_update(options);
4356      }
4357      return img;
4358    };
4359
4360
4361    /**
4362     * @override
4363     */
4364
4365    CloudinaryJQuery.prototype.responsive = function(options) {
4366      var ref, ref1, ref2, responsiveClass, responsiveConfig, responsiveResizeInitialized, responsive_resize, timeout;
4367      responsiveConfig = jQuery.extend(responsiveConfig || {}, options);
4368      responsiveClass = (ref = this.responsiveConfig['responsive_class']) != null ? ref : this.config('responsive_class');
4369      jQuery("img." + responsiveClass + ", img.cld-hidpi").cloudinary_update(responsiveConfig);
4370      responsive_resize = (ref1 = (ref2 = responsiveConfig['responsive_resize']) != null ? ref2 : this.config('responsive_resize')) != null ? ref1 : true;
4371      if (responsive_resize && !responsiveResizeInitialized) {
4372        responsiveConfig.resizing = responsiveResizeInitialized = true;
4373        timeout = null;
4374        return jQuery(window).on('resize', (function(_this) {
4375          return function() {
4376            var debounce, ref3, ref4, reset, run, wait;
4377            debounce = (ref3 = (ref4 = responsiveConfig['responsive_debounce']) != null ? ref4 : _this.config('responsive_debounce')) != null ? ref3 : 100;
4378            reset = function() {
4379              if (timeout) {
4380                clearTimeout(timeout);
4381                return timeout = null;
4382              }
4383            };
4384            run = function() {
4385              return jQuery("img." + responsiveClass).cloudinary_update(responsiveConfig);
4386            };
4387            wait = function() {
4388              reset();
4389              return setTimeout((function() {
4390                reset();
4391                return run();
4392              }), debounce);
4393            };
4394            if (debounce) {
4395              return wait();
4396            } else {
4397              return run();
4398            }
4399          };
4400        })(this));
4401      }
4402    };
4403
4404    return CloudinaryJQuery;
4405
4406  })(Cloudinary);
4407
4408  /**
4409   * The following methods are provided through the jQuery class
4410   * @class jQuery
4411   */
4412
4413  /**
4414   * Convert all img tags in the collection to utilize Cloudinary.
4415   * @function jQuery#cloudinary
4416   * @param {Object} [options] - options for the tag and transformations
4417   * @returns {jQuery}
4418   */
4419  jQuery.fn.cloudinary = function(options) {
4420    this.filter('img').each(function() {
4421      var img_options, public_id, url;
4422      img_options = jQuery.extend({
4423        width: jQuery(this).attr('width'),
4424        height: jQuery(this).attr('height'),
4425        src: jQuery(this).attr('src')
4426      }, jQuery(this).data(), options);
4427      public_id = img_options.source || img_options.src;
4428      delete img_options.source;
4429      delete img_options.src;
4430      url = jQuery.cloudinary.url(public_id, img_options);
4431      img_options = new Transformation(img_options).toHtmlAttributes();
4432      return jQuery(this).data('src-cache', url).attr({
4433        width: img_options.width,
4434        height: img_options.height
4435      });
4436    }).cloudinary_update(options);
4437    return this;
4438  };
4439
4440  /**
4441   * Update hidpi (dpr_auto) and responsive (w_auto) fields according to the current container size and the device pixel ratio.
4442   * Only images marked with the cld-responsive class have w_auto updated.
4443   * options:
4444   * - responsive_use_stoppoints:
4445   *   - true - always use stoppoints for width
4446   *   - "resize" - use exact width on first render and stoppoints on resize (default)
4447   *   - false - always use exact width
4448   * - responsive:
4449   *   - true - enable responsive on this element. Can be done by adding cld-responsive.
4450   *            Note that jQuery.cloudinary.responsive() should be called once on the page.
4451   * - responsive_preserve_height: if set to true, original css height is perserved. Should only be used if the transformation supports different aspect ratios.
4452   */
4453  jQuery.fn.cloudinary_update = function(options) {
4454    if (options == null) {
4455      options = {};
4456    }
4457    jQuery.cloudinary.cloudinary_update(this.filter('img').toArray(), options);
4458    return this;
4459  };
4460  webp = null;
4461
4462  /**
4463   * @function jQuery#webpify
4464   */
4465  jQuery.fn.webpify = function(options, webp_options) {
4466    var that, webp_canary;
4467    if (options == null) {
4468      options = {};
4469    }
4470    that = this;
4471    webp_options = webp_options != null ? webp_options : options;
4472    if (!webp) {
4473      webp = jQuery.Deferred();
4474      webp_canary = new Image;
4475      webp_canary.onerror = webp.reject;
4476      webp_canary.onload = webp.resolve;
4477      webp_canary.src = 'data:image/webp;base64,UklGRi4AAABXRUJQVlA4TCEAAAAvAUAAEB8wAiMwAgSSNtse/cXjxyCCmrYNWPwmHRH9jwMA';
4478    }
4479    jQuery(function() {
4480      return webp.done(function() {
4481        return jQuery(that).cloudinary(jQuery.extend({}, webp_options, {
4482          format: 'webp'
4483        }));
4484      }).fail(function() {
4485        return jQuery(that).cloudinary(options);
4486      });
4487    });
4488    return this;
4489  };
4490  jQuery.fn.fetchify = function(options) {
4491    return this.cloudinary(jQuery.extend(options, {
4492      'type': 'fetch'
4493    }));
4494  };
4495  jQuery.cloudinary = new CloudinaryJQuery();
4496  jQuery.cloudinary.fromDocument();
4497
4498  /**
4499   * This module extends CloudinaryJquery to support jQuery File Upload
4500   * Depends on 'jquery', 'util', 'cloudinaryjquery', 'jquery.ui.widget', 'jquery.iframe-transport','jquery.fileupload'
4501   */
4502
4503  /**
4504   * Delete a resource using the upload token
4505   * @function CloudinaryJQuery#delete_by_token
4506   * @param {string} delete_token - the delete token
4507   * @param {Object} [options]
4508   * @param {string} [options.url] - an alternative URL to use for the API
4509   * @param {string} [options.cloud_name] - an alternative cloud_name to use. This parameter is ignored if `options.url` is provided.
4510   */
4511  CloudinaryJQuery.prototype.delete_by_token = function(delete_token, options) {
4512    var cloud_name, dataType, url;
4513    options = options || {};
4514    url = options.url;
4515    if (!url) {
4516      cloud_name = options.cloud_name || jQuery.cloudinary.config().cloud_name;
4517      url = 'https://api.cloudinary.com/v1_1/' + cloud_name + '/delete_by_token';
4518    }
4519    dataType = jQuery.support.xhrFileUpload ? 'json' : 'iframe json';
4520    return jQuery.ajax({
4521      url: url,
4522      method: 'POST',
4523      data: {
4524        token: delete_token
4525      },
4526      headers: {
4527        'X-Requested-With': 'XMLHttpRequest'
4528      },
4529      dataType: dataType
4530    });
4531  };
4532
4533  /**
4534   * Creates an `input` tag and sets it up to upload files to cloudinary
4535   * @function CloudinaryJQuery#unsigned_upload_tag
4536   * @param {string}
4537   */
4538  CloudinaryJQuery.prototype.unsigned_upload_tag = function(upload_preset, upload_params, options) {
4539    return jQuery('<input/>').attr({
4540      type: 'file',
4541      name: 'file'
4542    }).unsigned_cloudinary_upload(upload_preset, upload_params, options);
4543  };
4544
4545  /**
4546   * Initialize the jQuery File Upload plugin to upload to Cloudinary
4547   * @function jQuery#cloudinary_fileupload
4548   * @param {Object} options
4549   * @returns {jQuery}
4550   */
4551  jQuery.fn.cloudinary_fileupload = function(options) {
4552    var cloud_name, initializing, resource_type, type, upload_url;
4553    if (!Util.isFunction(jQuery.fn.fileupload)) {
4554      return this;
4555    }
4556    initializing = !this.data('blueimpFileupload');
4557    if (initializing) {
4558      options = jQuery.extend({
4559        maxFileSize: 20000000,
4560        dataType: 'json',
4561        headers: {
4562          'X-Requested-With': 'XMLHttpRequest'
4563        }
4564      }, options);
4565    }
4566    this.fileupload(options);
4567    if (initializing) {
4568      this.bind('fileuploaddone', function(e, data) {
4569        var add_field, field, multiple, upload_info;
4570        if (data.result.error) {
4571          return;
4572        }
4573        data.result.path = ['v', data.result.version, '/', data.result.public_id, data.result.format ? '.' + data.result.format : ''].join('');
4574        if (data.cloudinaryField && data.form.length > 0) {
4575          upload_info = [data.result.resource_type, data.result.type, data.result.path].join('/') + '#' + data.result.signature;
4576          multiple = jQuery(e.target).prop('multiple');
4577          add_field = function() {
4578            return jQuery('<input/>').attr({
4579              type: 'hidden',
4580              name: data.cloudinaryField
4581            }).val(upload_info).appendTo(data.form);
4582          };
4583          if (multiple) {
4584            add_field();
4585          } else {
4586            field = jQuery(data.form).find('input[name="' + data.cloudinaryField + '"]');
4587            if (field.length > 0) {
4588              field.val(upload_info);
4589            } else {
4590              add_field();
4591            }
4592          }
4593        }
4594        return jQuery(e.target).trigger('cloudinarydone', data);
4595      });
4596      this.bind('fileuploadsend', function(e, data) {
4597        data.headers = jQuery.extend({}, data.headers, {
4598          'X-Unique-Upload-Id': (Math.random() * 10000000000).toString(16)
4599        });
4600        return true;
4601      });
4602      this.bind('fileuploadstart', function(e) {
4603        return jQuery(e.target).trigger('cloudinarystart');
4604      });
4605      this.bind('fileuploadstop', function(e) {
4606        return jQuery(e.target).trigger('cloudinarystop');
4607      });
4608      this.bind('fileuploadprogress', function(e, data) {
4609        return jQuery(e.target).trigger('cloudinaryprogress', data);
4610      });
4611      this.bind('fileuploadprogressall', function(e, data) {
4612        return jQuery(e.target).trigger('cloudinaryprogressall', data);
4613      });
4614      this.bind('fileuploadfail', function(e, data) {
4615        return jQuery(e.target).trigger('cloudinaryfail', data);
4616      });
4617      this.bind('fileuploadalways', function(e, data) {
4618        return jQuery(e.target).trigger('cloudinaryalways', data);
4619      });
4620      if (!this.fileupload('option').url) {
4621        cloud_name = options.cloud_name || jQuery.cloudinary.config().cloud_name;
4622        resource_type = options.resource_type || 'auto';
4623        type = options.type || 'upload';
4624        upload_url = 'https://api.cloudinary.com/v1_1/' + cloud_name + '/' + resource_type + '/' + type;
4625        this.fileupload('option', 'url', upload_url);
4626      }
4627    }
4628    return this;
4629  };
4630
4631  /**
4632   * Add a file to upload
4633   * @function jQuery#cloudinary_upload_url
4634   * @param {string} remote_url - the url to add
4635   * @returns {jQuery}
4636   */
4637  jQuery.fn.cloudinary_upload_url = function(remote_url) {
4638    if (!Util.isFunction(jQuery.fn.fileupload)) {
4639      return this;
4640    }
4641    this.fileupload('option', 'formData').file = remote_url;
4642    this.fileupload('add', {
4643      files: [remote_url]
4644    });
4645    delete this.fileupload('option', 'formData').file;
4646    return this;
4647  };
4648
4649  /**
4650   * Initialize the jQuery File Upload plugin to upload to Cloudinary using unsigned upload
4651   * @function jQuery#unsigned_cloudinary_upload
4652   * @param {string} upload_preset - the upload preset to use
4653   * @param {Object} [upload_params] - parameters that should be past to the server
4654   * @param {Object} [options]
4655   * @returns {jQuery}
4656   */
4657  jQuery.fn.unsigned_cloudinary_upload = function(upload_preset, upload_params, options) {
4658    var attr, attrs_to_move, html_options, i, key, value;
4659    if (upload_params == null) {
4660      upload_params = {};
4661    }
4662    if (options == null) {
4663      options = {};
4664    }
4665    upload_params = Util.cloneDeep(upload_params);
4666    options = Util.cloneDeep(options);
4667    attrs_to_move = ['cloud_name', 'resource_type', 'type'];
4668    i = 0;
4669    while (i < attrs_to_move.length) {
4670      attr = attrs_to_move[i];
4671      if (upload_params[attr]) {
4672        options[attr] = upload_params[attr];
4673        delete upload_params[attr];
4674      }
4675      i++;
4676    }
4677    for (key in upload_params) {
4678      value = upload_params[key];
4679      if (Util.isPlainObject(value)) {
4680        upload_params[key] = jQuery.map(value, function(v, k) {
4681          if (Util.isString(v)) {
4682            v = v.replace(/[\|=]/g, "\\$&");
4683          }
4684          return k + '=' + v;
4685        }).join('|');
4686      } else if (Util.isArray(value)) {
4687        if (value.length > 0 && jQuery.isArray(value[0])) {
4688          upload_params[key] = jQuery.map(value, function(array_value) {
4689            return array_value.join(',');
4690          }).join('|');
4691        } else {
4692          upload_params[key] = value.join(',');
4693        }
4694      }
4695    }
4696    if (!upload_params.callback) {
4697      upload_params.callback = '/cloudinary_cors.html';
4698    }
4699    upload_params.upload_preset = upload_preset;
4700    options.formData = upload_params;
4701    if (options.cloudinary_field) {
4702      options.cloudinaryField = options.cloudinary_field;
4703      delete options.cloudinary_field;
4704    }
4705    html_options = options.html || {};
4706    html_options["class"] = Util.trim("cloudinary_fileupload " + (html_options["class"] || ''));
4707    if (options.multiple) {
4708      html_options.multiple = true;
4709    }
4710    this.attr(html_options).cloudinary_fileupload(options);
4711    return this;
4712  };
4713  jQuery.cloudinary = new CloudinaryJQuery();
4714  cloudinary = {
4715    utf8_encode: utf8_encode,
4716    crc32: crc32,
4717    Util: Util,
4718    Condition: Condition,
4719    Transformation: Transformation,
4720    Configuration: Configuration,
4721    HtmlTag: HtmlTag,
4722    ImageTag: ImageTag,
4723    VideoTag: VideoTag,
4724    ClientHintsMetaTag: ClientHintsMetaTag,
4725    Layer: Layer,
4726    FetchLayer: FetchLayer,
4727    TextLayer: TextLayer,
4728    SubtitlesLayer: SubtitlesLayer,
4729    Cloudinary: Cloudinary,
4730    VERSION: "2.5.0",
4731    CloudinaryJQuery: CloudinaryJQuery
4732  };
4733  return cloudinary;
4734});
4735
4736

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.