Passed
Push — master ( 7e07d9...30706c )
by Ron
02:08 queued 12s
created

public/js/tinymce/plugins/imagetools/plugin.js   F

Complexity

Total Complexity 688
Complexity/F 1.51

Size

Lines of Code 3653
Function Count 457

Duplication

Duplicated Lines 369
Ratio 10.1 %

Importance

Changes 0
Metric Value
eloc 2852
nc 0
dl 369
loc 3653
c 0
b 0
f 0
cc 0
rs 0.8
wmc 688
mnd 4
bc 621
fnc 457
bpm 1.3588
cpm 1.5054
noi 57

211 Functions

Rating   Name   Duplication   Size   Complexity  
B ExifReader.constructor 0 188 1
A plugin.js ➔ isServiceErrorCode 0 3 1
A plugin.js ➔ appendApiKey 0 8 4
A plugin.js ➔ uriToBlob 0 9 3
A plugin.js ➔ createState 0 6 1
A plugin.js ➔ applyColorFilter 0 21 1
A plugin.js ➔ sepia 0 3 1
A ExifReader.SLONG 0 3 1
A plugin.js ➔ imageToBlob 0 7 2
A plugin.js ➔ dataUriToBlob 0 7 1
A plugin.js ➔ requestBlob 0 5 1
A plugin.js ➔ create$1 0 41 1
A plugin.js ➔ blobToDataUri 0 9 1
A plugin.js ➔ none 0 3 1
A plugin.js ➔ getCredentialsHosts 0 3 1
A plugin.js ➔ displayError 0 6 1
A plugin.js ➔ fromImage 0 5 1
A plugin.js ➔ exposure 0 3 1
A plugin.js ➔ create 0 3 1
A plugin.js ➔ traverse 0 7 2
A plugin.js ➔ findSelectedBlob 0 8 2
A plugin.js ➔ flip 0 5 1
A plugin.js ➔ identity$1 0 29 1
A plugin.js ➔ parseJson 0 8 1
A plugin.js ➔ imageResultToDataURL 0 3 1
A plugin.js ➔ isCorsImage 0 3 1
A plugin.js ➔ canvasToBlob 0 12 2
A plugin.js ➔ requestAnimationFrame 0 4 1
B plugin.js ➔ path 6 6 6
A plugin.js ➔ adjust 0 11 1
A plugin.js ➔ applyFlip 0 12 2
B plugin.js ➔ applyRotate 0 19 8
A plugin.js ➔ nul 3 3 1
A plugin.js ➔ colorize 0 3 1
A plugin.js ➔ exifRotate 0 22 1
A global.add(ꞌimagetoolsꞌ) 0 8 1
B plugin.js ➔ typeOf 0 10 6
A plugin.js ➔ grayscale 0 3 1
A plugin.js ➔ call$$1 3 3 1
A plugin.js ➔ blobToDataUri$1 0 3 1
A plugin.js ➔ noop$$1 2 2 1
B plugin.js ➔ scale 0 19 8
A plugin.js ➔ resize$2 0 3 1
A plugin.js ➔ getProxyUrl 0 3 1
A plugin.js ➔ revokeObjectURL 0 3 1
A plugin.js ➔ rotate$1 0 3 1
A plugin.js ➔ colorFilter 0 5 1
A plugin.js ➔ shouldReuseFilename 0 3 1
A plugin.js ➔ adjustBrightness 0 30 1
A plugin.js ➔ fromBlobAndUrlSync$1 0 3 1
A plugin.js ➔ loadImage$1 0 13 1
B BinaryReader.constructor 0 60 1
A BinaryReader.SEGMENT 0 11 4
A plugin.js ➔ eq 3 3 1
A plugin.js ➔ extractFilename 0 7 2
A plugin.js ➔ functionColorFilter 0 26 1
A plugin.js ➔ getWidth 0 3 1
A BinaryReader.CHAR 0 3 1
A ExifReader.UNDEFINED 0 3 1
B plugin.js ➔ setImageSize 0 18 6
A plugin.js ➔ _scale 0 12 1
A plugin.js ➔ getCorsHosts 0 3 1
A plugin.js ➔ getHttpErrorMsg 0 8 1
A ExifReader.SHORT 0 3 1
A ExifReader.TIFF 0 3 1
A plugin.js ➔ getOrDie 0 6 3
B NONE.constructor 53 53 2
A ExifReader.BYTE 0 3 1
A plugin.js ➔ uriToBlob$1 0 3 1
A plugin.js ➔ get2dContext 0 3 1
B plugin.js ➔ open 0 625 1
A ExifReader.thumb 0 13 3
A BinaryReader.LONG 0 3 1
A plugin.js ➔ fromCanvas 0 5 1
A plugin.js ➔ getUploadTimeout 0 3 1
A plugin.js ➔ url 0 3 1
A plugin.js ➔ isEditableImage 0 4 1
A BinaryReader.asArray 0 7 2
A plugin.js ➔ canvasToDataURL 0 6 1
A plugin.js ➔ imageResultToBlob 0 7 3
A plugin.js ➔ adjustSaturation 0 35 2
A plugin.js ➔ get3dContext 0 11 2
A plugin.js ➔ clamp 0 9 3
A plugin.js ➔ flip$2 0 7 1
B plugin.js ➔ getImageSize 0 26 7
A plugin.js ➔ readBlob 0 10 1
A ExifReader.EXIF 0 18 5
A plugin.js ➔ resolve 0 4 1
A plugin.js ➔ handleHttpError 0 4 1
A plugin.js ➔ blobToImageResult 0 3 1
A plugin.js ➔ imageResultToOriginalBlob 0 3 1
A plugin.js ➔ fromBlob 0 5 1
A BinaryReader.SHORT 0 3 1
A plugin.js ➔ createObjectURL 0 3 1
A plugin.js ➔ adjustHue 0 36 1
A plugin.js ➔ basicColorFilter 0 5 1
A plugin.js ➔ from 0 3 3
A plugin.js ➔ blobToBase64$1 0 3 1
A plugin.js ➔ isCorsWithCredentialsImage 0 3 1
A plugin.js ➔ edit 0 7 1
A plugin.js ➔ BinaryReader 0 4 1
A plugin.js ➔ crop 0 5 1
A me.getOrDie 3 3 1
A plugin.js ➔ Blob 0 4 1
A plugin.js ➔ Cell 0 17 1
A plugin.js ➔ sharpen 0 3 1
A plugin.js ➔ Uint8Array 0 4 1
A ExifReader.RATIONAL 0 3 1
A plugin.js ➔ applyCrop 0 6 1
A plugin.js ➔ isType 0 5 1
A plugin.js ➔ dataUriToBlobSync$1 0 3 1
B plugin.js ➔ applyConvoluteFilter 0 47 1
A plugin.js ➔ createId 0 3 1
A plugin.js ➔ isLocalImage 0 4 1
A plugin.js ➔ getHeight 0 3 1
A plugin.js ➔ isValue 0 3 1
B plugin.js ➔ ExifReader 0 34 7
A plugin.js ➔ adjustColors 0 32 1
A ExifReader.LONG 0 3 1
A plugin.js ➔ getNaturalImageSize 0 6 1
A plugin.js ➔ contrast 0 3 1
A plugin.js ➔ anyUriToBlob 26 26 1
A plugin.js ➔ setup 0 13 1
A plugin.js ➔ complexAdjustableColorFilter 0 5 1
B plugin.js ➔ adjustContrast 0 43 3
A plugin.js ➔ invert 0 3 1
B plugin.js ➔ promise 161 161 1
A plugin.js ➔ cancelTimedUpload 0 3 1
A ExifReader.ASCII 0 3 1
A ExifReader.GPS 0 15 4
A plugin.js ➔ resize 0 5 1
A ExifReader.asArray 0 7 2
A plugin.js ➔ atob 0 4 1
A plugin.js ➔ constant 0 5 1
A Filters.colorize 0 3 1
A plugin.js ➔ imageToImageResult 0 3 1
A plugin.js ➔ basicConvolutionFilter 0 5 1
A me.fold 3 3 1
A plugin.js ➔ blobToCanvas 0 10 1
A plugin.js ➔ blobToImage$1 0 3 1
A ExifReader.STRING 0 3 1
A plugin.js ➔ requestUrlAsBlob 0 21 1
A plugin.js ➔ handleServiceErrorResponse 0 3 2
B plugin.js ➔ create$2 0 160 1
A plugin.js ➔ getServiceError 0 9 2
A BinaryReader.read 0 10 4
A plugin.js ➔ register$1 0 27 1
A plugin.js ➔ curry 14 14 2
A plugin.js ➔ saturate 0 3 1
A BinaryReader.BYTE 0 3 1
A plugin.js ➔ gamma 0 3 1
A plugin.js ➔ adjustGrayscale 0 30 1
A plugin.js ➔ getUrl 0 3 2
A plugin.js ➔ imageToBlob$2 0 13 4
A plugin.js ➔ flip$1 0 3 1
A plugin.js ➔ adjustSepia 0 30 1
A plugin.js ➔ convoluteFilter 0 5 1
B plugin.js ➔ extractHeaders 0 26 8
A BinaryReader.STRING 0 3 1
A plugin.js ➔ register 0 11 1
A plugin.js ➔ getServiceErrorMsg 0 7 1
A ExifReader.length 0 3 1
A plugin.js ➔ dataUriToBlobSync 23 23 4
A plugin.js ➔ multiply 0 16 5
A plugin.js ➔ resize$1 0 7 1
A plugin.js ➔ destroyState 0 5 2
A ExifReader.SEGMENT 0 3 1
A plugin.js ➔ revokeImageUrl 0 3 1
A plugin.js ➔ rotate$2 0 12 1
A plugin.js ➔ startTimedUpload 0 6 1
A BinaryReader.length 0 3 2
A plugin.js ➔ blobToArrayBuffer 0 9 1
A plugin.js ➔ clone 0 7 1
A plugin.js ➔ brightness 0 3 1
A plugin.js ➔ imageToBlob$1 0 3 1
A plugin.js ➔ extractFrom 0 30 1
A plugin.js ➔ emboss 0 3 1
A plugin.js ➔ register$2 0 3 1
A plugin.js ➔ UndoStack 0 37 1
A plugin.js ➔ selectedImageOperation 0 9 1
A plugin.js ➔ requestServiceBlob 0 9 1
A plugin.js ➔ handleServiceError 0 6 1
A plugin.js ➔ XMLHttpRequest$1 0 4 1
A plugin.js ➔ hue 0 3 1
A plugin.js ➔ destroyStates 0 3 1
B plugin.js ➔ CropRect 0 264 1
A plugin.js ➔ FileReader 0 4 1
A plugin.js ➔ crop$1 0 3 1
A plugin.js ➔ undef 3 3 1
A ExifReader.CHAR 0 3 1
A plugin.js ➔ rotate 0 5 1
A BinaryReader.readByteAt 0 3 1
A BinaryReader.SLONG 0 4 2
A plugin.js ➔ find 0 9 3
A plugin.js ➔ getToolbarItems 0 3 1
A plugin.js ➔ updateSelectedImage 0 46 1
B imagetools.constructor 369 3651 4
A plugin.js ➔ ??? 0 3 1
A plugin.js ➔ getSelectedImage 0 3 1
A plugin.js ➔ getApiKey 0 3 1
A ExifReader.SRATIONAL 0 3 1
A plugin.js ➔ Plugin 0 2 1
A plugin.js ➔ id 3 3 1
B plugin.js ➔ some 62 62 1
A plugin.js ➔ blobToBase64 0 5 1
A plugin.js ➔ editImageDialog 0 28 1
A plugin.js ➔ unsafe 0 3 1
A me.toArray 3 3 1
A plugin.js ➔ blobToImage 24 24 1
A plugin.js ➔ fromBlobAndUrlSync 0 3 1
C ExifReader.extractTags 0 59 10

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like public/js/tinymce/plugins/imagetools/plugin.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
(function () {
2
var imagetools = (function () {
0 ignored issues
show
Unused Code introduced by
The variable imagetools seems to be never used. Consider removing it.
Loading history...
3
    'use strict';
4
5
    var Cell = function (initial) {
6
      var value = initial;
7
      var get = function () {
8
        return value;
9
      };
10
      var set = function (v) {
11
        value = v;
12
      };
13
      var clone = function () {
14
        return Cell(get());
15
      };
16
      return {
17
        get: get,
18
        set: set,
19
        clone: clone
20
      };
21
    };
22
23
    var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
0 ignored issues
show
Bug introduced by
The variable tinymce seems to be never declared. If this is a global, consider adding a /** global: tinymce */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
24
25
    var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
26
27
    function create(width, height) {
28
      return resize(document.createElement('canvas'), width, height);
29
    }
30
    function clone(canvas) {
31
      var tCanvas, ctx;
32
      tCanvas = create(canvas.width, canvas.height);
33
      ctx = get2dContext(tCanvas);
34
      ctx.drawImage(canvas, 0, 0);
35
      return tCanvas;
36
    }
37
    function get2dContext(canvas) {
38
      return canvas.getContext('2d');
39
    }
40
    function get3dContext(canvas) {
41
      var gl = null;
0 ignored issues
show
Unused Code introduced by
The assignment to gl seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
42
      try {
43
        gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
44
      } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
45
      }
46
      if (!gl) {
47
        gl = null;
48
      }
49
      return gl;
50
    }
51
    function resize(canvas, width, height) {
52
      canvas.width = width;
53
      canvas.height = height;
54
      return canvas;
55
    }
56
    var Canvas = {
57
      create: create,
58
      clone: clone,
59
      resize: resize,
60
      get2dContext: get2dContext,
61
      get3dContext: get3dContext
62
    };
63
64
    function getWidth(image) {
65
      return image.naturalWidth || image.width;
66
    }
67
    function getHeight(image) {
68
      return image.naturalHeight || image.height;
69
    }
70
    var ImageSize = {
71
      getWidth: getWidth,
72
      getHeight: getHeight
73
    };
74
75 View Code Duplication
    var promise = function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
76
      var Promise = function (fn) {
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Promise. This makes code hard to read, consider using a different name.
Loading history...
77
        if (typeof this !== 'object')
78
          throw new TypeError('Promises must be constructed via new');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
79
        if (typeof fn !== 'function')
80
          throw new TypeError('not a function');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
81
        this._state = null;
82
        this._value = null;
83
        this._deferreds = [];
84
        doResolve(fn, bind(resolve, this), bind(reject, this));
85
      };
86
      var asap = Promise.immediateFn || typeof window.setImmediate === 'function' && window.setImmediate || function (fn) {
87
        setTimeout(fn, 1);
88
      };
89
      function bind(fn, thisArg) {
90
        return function () {
91
          fn.apply(thisArg, arguments);
92
        };
93
      }
94
      var isArray = Array.isArray || function (value) {
95
        return Object.prototype.toString.call(value) === '[object Array]';
96
      };
97
      function handle(deferred) {
98
        var me = this;
99
        if (this._state === null) {
100
          this._deferreds.push(deferred);
101
          return;
102
        }
103
        asap(function () {
104
          var cb = me._state ? deferred.onFulfilled : deferred.onRejected;
105
          if (cb === null) {
106
            (me._state ? deferred.resolve : deferred.reject)(me._value);
107
            return;
108
          }
109
          var ret;
110
          try {
111
            ret = cb(me._value);
112
          } catch (e) {
113
            deferred.reject(e);
114
            return;
115
          }
116
          deferred.resolve(ret);
117
        });
118
      }
119
      function resolve(newValue) {
120
        try {
121
          if (newValue === this)
122
            throw new TypeError('A promise cannot be resolved with itself.');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
123
          if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
124
            var then = newValue.then;
125
            if (typeof then === 'function') {
126
              doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this));
127
              return;
128
            }
129
          }
130
          this._state = true;
131
          this._value = newValue;
132
          finale.call(this);
133
        } catch (e) {
134
          reject.call(this, e);
135
        }
136
      }
137
      function reject(newValue) {
138
        this._state = false;
139
        this._value = newValue;
140
        finale.call(this);
141
      }
142
      function finale() {
143
        for (var i = 0, len = this._deferreds.length; i < len; i++) {
144
          handle.call(this, this._deferreds[i]);
145
        }
146
        this._deferreds = null;
147
      }
148
      function Handler(onFulfilled, onRejected, resolve, reject) {
149
        this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
150
        this.onRejected = typeof onRejected === 'function' ? onRejected : null;
151
        this.resolve = resolve;
152
        this.reject = reject;
153
      }
154
      function doResolve(fn, onFulfilled, onRejected) {
155
        var done = false;
156
        try {
157
          fn(function (value) {
158
            if (done)
159
              return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
160
            done = true;
161
            onFulfilled(value);
162
          }, function (reason) {
163
            if (done)
164
              return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
165
            done = true;
166
            onRejected(reason);
167
          });
168
        } catch (ex) {
169
          if (done)
170
            return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
171
          done = true;
172
          onRejected(ex);
173
        }
174
      }
175
      Promise.prototype['catch'] = function (onRejected) {
176
        return this.then(null, onRejected);
177
      };
178
      Promise.prototype.then = function (onFulfilled, onRejected) {
179
        var me = this;
180
        return new Promise(function (resolve, reject) {
181
          handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject));
182
        });
183
      };
184
      Promise.all = function () {
185
        var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments);
186
        return new Promise(function (resolve, reject) {
187
          if (args.length === 0)
188
            return resolve([]);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
189
          var remaining = args.length;
190
          function res(i, val) {
191
            try {
192
              if (val && (typeof val === 'object' || typeof val === 'function')) {
193
                var then = val.then;
194
                if (typeof then === 'function') {
195
                  then.call(val, function (val) {
196
                    res(i, val);
197
                  }, reject);
198
                  return;
199
                }
200
              }
201
              args[i] = val;
202
              if (--remaining === 0) {
203
                resolve(args);
204
              }
205
            } catch (ex) {
206
              reject(ex);
207
            }
208
          }
209
          for (var i = 0; i < args.length; i++) {
210
            res(i, args[i]);
211
          }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
212
        });
213
      };
214
      Promise.resolve = function (value) {
215
        if (value && typeof value === 'object' && value.constructor === Promise) {
216
          return value;
217
        }
218
        return new Promise(function (resolve) {
219
          resolve(value);
220
        });
221
      };
222
      Promise.reject = function (value) {
223
        return new Promise(function (resolve, reject) {
224
          reject(value);
225
        });
226
      };
227
      Promise.race = function (values) {
228
        return new Promise(function (resolve, reject) {
229
          for (var i = 0, len = values.length; i < len; i++) {
230
            values[i].then(resolve, reject);
231
          }
232
        });
233
      };
234
      return Promise;
235
    };
236
    var Promise = window.Promise ? window.Promise : promise();
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Promise. This makes code hard to read, consider using a different name.
Loading history...
237
238
    var constant = function (value) {
239
      return function () {
240
        return value;
241
      };
242
    };
243 View Code Duplication
    function curry(fn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
244
      var initialArgs = [];
245
      for (var _i = 1; _i < arguments.length; _i++) {
246
        initialArgs[_i - 1] = arguments[_i];
247
      }
248
      return function () {
249
        var restArgs = [];
250
        for (var _i = 0; _i < arguments.length; _i++) {
251
          restArgs[_i] = arguments[_i];
252
        }
253
        var all = initialArgs.concat(restArgs);
254
        return fn.apply(null, all);
255
      };
256
    }
257
    var never = constant(false);
258
    var always = constant(true);
259
260
    var never$1 = never;
261
    var always$1 = always;
262
    var none = function () {
263
      return NONE;
264
    };
265 View Code Duplication
    var NONE = function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
266
      var eq = function (o) {
267
        return o.isNone();
268
      };
269
      var call$$1 = function (thunk) {
270
        return thunk();
271
      };
272
      var id = function (n) {
273
        return n;
274
      };
275
      var noop$$1 = function () {
276
      };
277
      var nul = function () {
278
        return null;
279
      };
280
      var undef = function () {
281
        return undefined;
282
      };
283
      var me = {
284
        fold: function (n, s) {
0 ignored issues
show
Unused Code introduced by
The parameter s is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
285
          return n();
286
        },
287
        is: never$1,
288
        isSome: never$1,
289
        isNone: always$1,
290
        getOr: id,
291
        getOrThunk: call$$1,
292
        getOrDie: function (msg) {
293
          throw new Error(msg || 'error: getOrDie called on none.');
294
        },
295
        getOrNull: nul,
296
        getOrUndefined: undef,
297
        or: id,
298
        orThunk: call$$1,
299
        map: none,
300
        ap: none,
301
        each: noop$$1,
302
        bind: none,
303
        flatten: none,
304
        exists: never$1,
305
        forall: always$1,
306
        filter: none,
307
        equals: eq,
308
        equals_: eq,
309
        toArray: function () {
310
          return [];
311
        },
312
        toString: constant('none()')
313
      };
314
      if (Object.freeze)
315
        Object.freeze(me);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
316
      return me;
317
    }();
318 View Code Duplication
    var some = function (a) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
319
      var constant_a = function () {
320
        return a;
321
      };
322
      var self = function () {
323
        return me;
324
      };
325
      var map = function (f) {
326
        return some(f(a));
327
      };
328
      var bind = function (f) {
329
        return f(a);
330
      };
331
      var me = {
332
        fold: function (n, s) {
333
          return s(a);
334
        },
335
        is: function (v) {
336
          return a === v;
337
        },
338
        isSome: always$1,
339
        isNone: never$1,
340
        getOr: constant_a,
341
        getOrThunk: constant_a,
342
        getOrDie: constant_a,
343
        getOrNull: constant_a,
344
        getOrUndefined: constant_a,
345
        or: self,
346
        orThunk: self,
347
        map: map,
348
        ap: function (optfab) {
349
          return optfab.fold(none, function (fab) {
350
            return some(fab(a));
351
          });
352
        },
353
        each: function (f) {
354
          f(a);
355
        },
356
        bind: bind,
357
        flatten: constant_a,
358
        exists: bind,
359
        forall: bind,
360
        filter: function (f) {
361
          return f(a) ? me : NONE;
362
        },
363
        equals: function (o) {
364
          return o.is(a);
365
        },
366
        equals_: function (o, elementEq) {
367
          return o.fold(never$1, function (b) {
368
            return elementEq(a, b);
369
          });
370
        },
371
        toArray: function () {
372
          return [a];
373
        },
374
        toString: function () {
375
          return 'some(' + a + ')';
376
        }
377
      };
378
      return me;
379
    };
380
    var from = function (value) {
381
      return value === null || value === undefined ? NONE : some(value);
382
    };
383
    var Option = {
384
      some: some,
385
      none: none,
386
      from: from
387
    };
388
389
    var Global = typeof window !== 'undefined' ? window : Function('return this;')();
0 ignored issues
show
Performance Best Practice introduced by
Using new Function() to create a function is slow and difficult to debug. Such functions do not create a closure. Consider using another way to define your function.
Loading history...
390
391 View Code Duplication
    var path = function (parts, scope) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
392
      var o = scope !== undefined && scope !== null ? scope : Global;
393
      for (var i = 0; i < parts.length && o !== undefined && o !== null; ++i)
394
        o = o[parts[i]];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
395
      return o;
396
    };
397
    var resolve = function (p, scope) {
398
      var parts = p.split('.');
399
      return path(parts, scope);
400
    };
401
402
    var unsafe = function (name, scope) {
403
      return resolve(name, scope);
404
    };
405
    var getOrDie = function (name, scope) {
406
      var actual = unsafe(name, scope);
407
      if (actual === undefined || actual === null)
408
        throw name + ' not available on this browser';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
409
      return actual;
410
    };
411
    var Global$1 = { getOrDie: getOrDie };
412
413
    function Blob (parts, properties) {
414
      var f = Global$1.getOrDie('Blob');
415
      return new f(parts, properties);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like f should be capitalized.
Loading history...
416
    }
417
418
    function FileReader () {
419
      var f = Global$1.getOrDie('FileReader');
420
      return new f();
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like f should be capitalized.
Loading history...
421
    }
422
423
    function Uint8Array (arr) {
424
      var f = Global$1.getOrDie('Uint8Array');
425
      return new f(arr);
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like f should be capitalized.
Loading history...
426
    }
427
428
    var requestAnimationFrame = function (callback) {
429
      var f = Global$1.getOrDie('requestAnimationFrame');
430
      f(callback);
431
    };
432
    var atob = function (base64) {
433
      var f = Global$1.getOrDie('atob');
434
      return f(base64);
435
    };
436
    var Window = {
437
      atob: atob,
438
      requestAnimationFrame: requestAnimationFrame
439
    };
440
441
    function imageToBlob(image) {
442
      var src = image.src;
443
      if (src.indexOf('data:') === 0) {
444
        return dataUriToBlob(src);
445
      }
446
      return anyUriToBlob(src);
447
    }
448 View Code Duplication
    function blobToImage(blob) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
449
      return new Promise(function (resolve, reject) {
450
        var blobUrl = URL.createObjectURL(blob);
0 ignored issues
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
451
        var image = new Image();
0 ignored issues
show
Bug introduced by
The variable Image seems to be never declared. If this is a global, consider adding a /** global: Image */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
452
        var removeListeners = function () {
453
          image.removeEventListener('load', loaded);
454
          image.removeEventListener('error', error);
455
        };
456
        function loaded() {
457
          removeListeners();
458
          resolve(image);
459
        }
460
        function error() {
461
          removeListeners();
462
          reject('Unable to load data of type ' + blob.type + ': ' + blobUrl);
463
        }
464
        image.addEventListener('load', loaded);
465
        image.addEventListener('error', error);
466
        image.src = blobUrl;
467
        if (image.complete) {
468
          loaded();
469
        }
470
      });
471
    }
472 View Code Duplication
    function anyUriToBlob(url) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
473
      return new Promise(function (resolve, reject) {
474
        var xhr = new XMLHttpRequest();
475
        xhr.open('GET', url, true);
476
        xhr.responseType = 'blob';
477
        xhr.onload = function () {
478
          if (this.status == 200) {
479
            resolve(this.response);
480
          }
481
        };
482
        xhr.onerror = function () {
483
          var _this = this;
484
          var corsError = function () {
485
            var obj = new Error('No access to download image');
486
            obj.code = 18;
487
            obj.name = 'SecurityError';
488
            return obj;
489
          };
490
          var genericError = function () {
491
            return new Error('Error ' + _this.status + ' downloading image');
492
          };
493
          reject(this.status === 0 ? corsError() : genericError());
494
        };
495
        xhr.send();
496
      });
497
    }
498 View Code Duplication
    function dataUriToBlobSync(uri) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
499
      var data = uri.split(',');
500
      var matches = /data:([^;]+)/.exec(data[0]);
501
      if (!matches)
502
        return Option.none();
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
503
      var mimetype = matches[1];
504
      var base64 = data[1];
505
      var sliceSize = 1024;
506
      var byteCharacters = Window.atob(base64);
507
      var bytesLength = byteCharacters.length;
508
      var slicesCount = Math.ceil(bytesLength / sliceSize);
509
      var byteArrays = new Array(slicesCount);
0 ignored issues
show
Coding Style Best Practice introduced by
Using the Array constructor is generally discouraged. Consider using an array literal instead.
Loading history...
510
      for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
511
        var begin = sliceIndex * sliceSize;
512
        var end = Math.min(begin + sliceSize, bytesLength);
513
        var bytes = new Array(end - begin);
514
        for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
515
          bytes[i] = byteCharacters[offset].charCodeAt(0);
516
        }
517
        byteArrays[sliceIndex] = Uint8Array(bytes);
518
      }
519
      return Option.some(Blob(byteArrays, { type: mimetype }));
520
    }
521
    function dataUriToBlob(uri) {
522
      return new Promise(function (resolve, reject) {
523
        dataUriToBlobSync(uri).fold(function () {
524
          reject('uri is not base64: ' + uri);
525
        }, resolve);
526
      });
527
    }
528
    function uriToBlob(url) {
529
      if (url.indexOf('blob:') === 0) {
530
        return anyUriToBlob(url);
531
      }
532
      if (url.indexOf('data:') === 0) {
533
        return dataUriToBlob(url);
534
      }
535
      return null;
536
    }
537
    function canvasToBlob(canvas, type, quality) {
538
      type = type || 'image/png';
539
      if (HTMLCanvasElement.prototype.toBlob) {
0 ignored issues
show
Bug introduced by
The variable HTMLCanvasElement seems to be never declared. If this is a global, consider adding a /** global: HTMLCanvasElement */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
540
        return new Promise(function (resolve) {
541
          canvas.toBlob(function (blob) {
542
            resolve(blob);
543
          }, type, quality);
544
        });
545
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
546
        return dataUriToBlob(canvas.toDataURL(type, quality));
547
      }
548
    }
549
    function canvasToDataURL(getCanvas, type, quality) {
550
      type = type || 'image/png';
551
      return getCanvas.then(function (canvas) {
552
        return canvas.toDataURL(type, quality);
553
      });
554
    }
555
    function blobToCanvas(blob) {
556
      return blobToImage(blob).then(function (image) {
557
        revokeImageUrl(image);
558
        var context, canvas;
559
        canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image));
560
        context = Canvas.get2dContext(canvas);
561
        context.drawImage(image, 0, 0);
562
        return canvas;
563
      });
564
    }
565
    function blobToDataUri(blob) {
566
      return new Promise(function (resolve) {
567
        var reader = FileReader();
568
        reader.onloadend = function () {
569
          resolve(reader.result);
570
        };
571
        reader.readAsDataURL(blob);
572
      });
573
    }
574
    function blobToArrayBuffer(blob) {
575
      return new Promise(function (resolve) {
576
        var reader = FileReader();
577
        reader.onloadend = function () {
578
          resolve(reader.result);
579
        };
580
        reader.readAsArrayBuffer(blob);
581
      });
582
    }
583
    function blobToBase64(blob) {
584
      return blobToDataUri(blob).then(function (dataUri) {
585
        return dataUri.split(',')[1];
586
      });
587
    }
588
    function revokeImageUrl(image) {
589
      URL.revokeObjectURL(image.src);
0 ignored issues
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
590
    }
591
    var Conversions = {
592
      blobToImage: blobToImage,
593
      imageToBlob: imageToBlob,
594
      blobToArrayBuffer: blobToArrayBuffer,
595
      blobToDataUri: blobToDataUri,
596
      blobToBase64: blobToBase64,
597
      dataUriToBlobSync: dataUriToBlobSync,
598
      canvasToBlob: canvasToBlob,
599
      canvasToDataURL: canvasToDataURL,
600
      blobToCanvas: blobToCanvas,
601
      uriToBlob: uriToBlob
602
    };
603
604
    var blobToImage$1 = function (image) {
605
      return Conversions.blobToImage(image);
606
    };
607
    var imageToBlob$1 = function (blob) {
608
      return Conversions.imageToBlob(blob);
609
    };
610
    var blobToDataUri$1 = function (blob) {
611
      return Conversions.blobToDataUri(blob);
612
    };
613
    var blobToBase64$1 = function (blob) {
614
      return Conversions.blobToBase64(blob);
615
    };
616
    var dataUriToBlobSync$1 = function (uri) {
617
      return Conversions.dataUriToBlobSync(uri);
618
    };
619
    var uriToBlob$1 = function (uri) {
620
      return Option.from(Conversions.uriToBlob(uri));
621
    };
622
    var BlobConversions = {
623
      blobToImage: blobToImage$1,
624
      imageToBlob: imageToBlob$1,
625
      blobToDataUri: blobToDataUri$1,
626
      blobToBase64: blobToBase64$1,
627
      dataUriToBlobSync: dataUriToBlobSync$1,
628
      uriToBlob: uriToBlob$1
629
    };
630
631
    function create$1(getCanvas, blob, uri) {
632
      var initialType = blob.type;
633
      var getType = constant(initialType);
634
      function toBlob() {
635
        return Promise.resolve(blob);
636
      }
637
      function toDataURL() {
638
        return uri;
639
      }
640
      function toBase64() {
641
        return uri.split(',')[1];
642
      }
643
      function toAdjustedBlob(type, quality) {
644
        return getCanvas.then(function (canvas) {
645
          return Conversions.canvasToBlob(canvas, type, quality);
646
        });
647
      }
648
      function toAdjustedDataURL(type, quality) {
649
        return getCanvas.then(function (canvas) {
650
          return Conversions.canvasToDataURL(canvas, type, quality);
651
        });
652
      }
653
      function toAdjustedBase64(type, quality) {
654
        return toAdjustedDataURL(type, quality).then(function (dataurl) {
655
          return dataurl.split(',')[1];
656
        });
657
      }
658
      function toCanvas() {
659
        return getCanvas.then(Canvas.clone);
660
      }
661
      return {
662
        getType: getType,
663
        toBlob: toBlob,
664
        toDataURL: toDataURL,
665
        toBase64: toBase64,
666
        toAdjustedBlob: toAdjustedBlob,
667
        toAdjustedDataURL: toAdjustedDataURL,
668
        toAdjustedBase64: toAdjustedBase64,
669
        toCanvas: toCanvas
670
      };
671
    }
672
    function fromBlob(blob) {
673
      return Conversions.blobToDataUri(blob).then(function (uri) {
674
        return create$1(Conversions.blobToCanvas(blob), blob, uri);
675
      });
676
    }
677
    function fromCanvas(canvas, type) {
678
      return Conversions.canvasToBlob(canvas, type).then(function (blob) {
679
        return create$1(Promise.resolve(canvas), blob, canvas.toDataURL());
680
      });
681
    }
682
    function fromImage(image) {
683
      return Conversions.imageToBlob(image).then(function (blob) {
684
        return fromBlob(blob);
685
      });
686
    }
687
    var fromBlobAndUrlSync = function (blob, url) {
688
      return create$1(Conversions.blobToCanvas(blob), blob, url);
689
    };
690
    var ImageResult = {
691
      fromBlob: fromBlob,
692
      fromCanvas: fromCanvas,
693
      fromImage: fromImage,
694
      fromBlobAndUrlSync: fromBlobAndUrlSync
695
    };
696
697
    function clamp(value, min, max) {
698
      value = parseFloat(value);
699
      if (value > max) {
700
        value = max;
701
      } else if (value < min) {
702
        value = min;
703
      }
704
      return value;
705
    }
706
    function identity$1() {
707
      return [
708
        1,
709
        0,
710
        0,
711
        0,
712
        0,
713
        0,
714
        1,
715
        0,
716
        0,
717
        0,
718
        0,
719
        0,
720
        1,
721
        0,
722
        0,
723
        0,
724
        0,
725
        0,
726
        1,
727
        0,
728
        0,
729
        0,
730
        0,
731
        0,
732
        1
733
      ];
734
    }
735
    var DELTA_INDEX = [
736
      0,
737
      0.01,
738
      0.02,
739
      0.04,
740
      0.05,
741
      0.06,
742
      0.07,
743
      0.08,
744
      0.1,
745
      0.11,
746
      0.12,
747
      0.14,
748
      0.15,
749
      0.16,
750
      0.17,
751
      0.18,
752
      0.2,
753
      0.21,
754
      0.22,
755
      0.24,
756
      0.25,
757
      0.27,
758
      0.28,
759
      0.3,
760
      0.32,
761
      0.34,
762
      0.36,
763
      0.38,
764
      0.4,
765
      0.42,
766
      0.44,
767
      0.46,
768
      0.48,
769
      0.5,
770
      0.53,
771
      0.56,
772
      0.59,
773
      0.62,
774
      0.65,
775
      0.68,
776
      0.71,
777
      0.74,
778
      0.77,
779
      0.8,
780
      0.83,
781
      0.86,
782
      0.89,
783
      0.92,
784
      0.95,
785
      0.98,
786
      1,
787
      1.06,
788
      1.12,
789
      1.18,
790
      1.24,
791
      1.3,
792
      1.36,
793
      1.42,
794
      1.48,
795
      1.54,
796
      1.6,
797
      1.66,
798
      1.72,
799
      1.78,
800
      1.84,
801
      1.9,
802
      1.96,
803
      2,
804
      2.12,
805
      2.25,
806
      2.37,
807
      2.5,
808
      2.62,
809
      2.75,
810
      2.87,
811
      3,
812
      3.2,
813
      3.4,
814
      3.6,
815
      3.8,
816
      4,
817
      4.3,
818
      4.7,
819
      4.9,
820
      5,
821
      5.5,
822
      6,
823
      6.5,
824
      6.8,
825
      7,
826
      7.3,
827
      7.5,
828
      7.8,
829
      8,
830
      8.4,
831
      8.7,
832
      9,
833
      9.4,
834
      9.6,
835
      9.8,
836
      10
837
    ];
838
    function multiply(matrix1, matrix2) {
839
      var i, j, k, val, col = [], out = new Array(10);
840
      for (i = 0; i < 5; i++) {
841
        for (j = 0; j < 5; j++) {
842
          col[j] = matrix2[j + i * 5];
843
        }
844
        for (j = 0; j < 5; j++) {
845
          val = 0;
846
          for (k = 0; k < 5; k++) {
847
            val += matrix1[j + k * 5] * col[k];
848
          }
849
          out[j + i * 5] = val;
850
        }
851
      }
852
      return out;
853
    }
854
    function adjust(matrix, adjustValue) {
855
      adjustValue = clamp(adjustValue, 0, 1);
856
      return matrix.map(function (value, index) {
857
        if (index % 6 === 0) {
858
          value = 1 - (1 - value) * adjustValue;
859
        } else {
860
          value *= adjustValue;
861
        }
862
        return clamp(value, 0, 1);
863
      });
864
    }
865
    function adjustContrast(matrix, value) {
866
      var x;
867
      value = clamp(value, -1, 1);
868
      value *= 100;
869
      if (value < 0) {
870
        x = 127 + value / 100 * 127;
871
      } else {
872
        x = value % 1;
873
        if (x === 0) {
874
          x = DELTA_INDEX[value];
875
        } else {
876
          x = DELTA_INDEX[Math.floor(value)] * (1 - x) + DELTA_INDEX[Math.floor(value) + 1] * x;
877
        }
878
        x = x * 127 + 127;
879
      }
880
      return multiply(matrix, [
881
        x / 127,
882
        0,
883
        0,
884
        0,
885
        0.5 * (127 - x),
886
        0,
887
        x / 127,
888
        0,
889
        0,
890
        0.5 * (127 - x),
891
        0,
892
        0,
893
        x / 127,
894
        0,
895
        0.5 * (127 - x),
896
        0,
897
        0,
898
        0,
899
        1,
900
        0,
901
        0,
902
        0,
903
        0,
904
        0,
905
        1
906
      ]);
907
    }
908
    function adjustSaturation(matrix, value) {
909
      var x, lumR, lumG, lumB;
910
      value = clamp(value, -1, 1);
911
      x = 1 + (value > 0 ? 3 * value : value);
912
      lumR = 0.3086;
913
      lumG = 0.6094;
914
      lumB = 0.082;
915
      return multiply(matrix, [
916
        lumR * (1 - x) + x,
917
        lumG * (1 - x),
918
        lumB * (1 - x),
919
        0,
920
        0,
921
        lumR * (1 - x),
922
        lumG * (1 - x) + x,
923
        lumB * (1 - x),
924
        0,
925
        0,
926
        lumR * (1 - x),
927
        lumG * (1 - x),
928
        lumB * (1 - x) + x,
929
        0,
930
        0,
931
        0,
932
        0,
933
        0,
934
        1,
935
        0,
936
        0,
937
        0,
938
        0,
939
        0,
940
        1
941
      ]);
942
    }
943
    function adjustHue(matrix, angle) {
944
      var cosVal, sinVal, lumR, lumG, lumB;
945
      angle = clamp(angle, -180, 180) / 180 * Math.PI;
946
      cosVal = Math.cos(angle);
947
      sinVal = Math.sin(angle);
948
      lumR = 0.213;
949
      lumG = 0.715;
950
      lumB = 0.072;
951
      return multiply(matrix, [
952
        lumR + cosVal * (1 - lumR) + sinVal * -lumR,
953
        lumG + cosVal * -lumG + sinVal * -lumG,
954
        lumB + cosVal * -lumB + sinVal * (1 - lumB),
955
        0,
956
        0,
957
        lumR + cosVal * -lumR + sinVal * 0.143,
958
        lumG + cosVal * (1 - lumG) + sinVal * 0.14,
959
        lumB + cosVal * -lumB + sinVal * -0.283,
960
        0,
961
        0,
962
        lumR + cosVal * -lumR + sinVal * -(1 - lumR),
963
        lumG + cosVal * -lumG + sinVal * lumG,
964
        lumB + cosVal * (1 - lumB) + sinVal * lumB,
965
        0,
966
        0,
967
        0,
968
        0,
969
        0,
970
        1,
971
        0,
972
        0,
973
        0,
974
        0,
975
        0,
976
        1
977
      ]);
978
    }
979
    function adjustBrightness(matrix, value) {
980
      value = clamp(255 * value, -255, 255);
981
      return multiply(matrix, [
982
        1,
983
        0,
984
        0,
985
        0,
986
        value,
987
        0,
988
        1,
989
        0,
990
        0,
991
        value,
992
        0,
993
        0,
994
        1,
995
        0,
996
        value,
997
        0,
998
        0,
999
        0,
1000
        1,
1001
        0,
1002
        0,
1003
        0,
1004
        0,
1005
        0,
1006
        1
1007
      ]);
1008
    }
1009
    function adjustColors(matrix, adjustR, adjustG, adjustB) {
1010
      adjustR = clamp(adjustR, 0, 2);
1011
      adjustG = clamp(adjustG, 0, 2);
1012
      adjustB = clamp(adjustB, 0, 2);
1013
      return multiply(matrix, [
1014
        adjustR,
1015
        0,
1016
        0,
1017
        0,
1018
        0,
1019
        0,
1020
        adjustG,
1021
        0,
1022
        0,
1023
        0,
1024
        0,
1025
        0,
1026
        adjustB,
1027
        0,
1028
        0,
1029
        0,
1030
        0,
1031
        0,
1032
        1,
1033
        0,
1034
        0,
1035
        0,
1036
        0,
1037
        0,
1038
        1
1039
      ]);
1040
    }
1041
    function adjustSepia(matrix, value) {
1042
      value = clamp(value, 0, 1);
1043
      return multiply(matrix, adjust([
1044
        0.393,
1045
        0.769,
1046
        0.189,
1047
        0,
1048
        0,
1049
        0.349,
1050
        0.686,
1051
        0.168,
1052
        0,
1053
        0,
1054
        0.272,
1055
        0.534,
1056
        0.131,
1057
        0,
1058
        0,
1059
        0,
1060
        0,
1061
        0,
1062
        1,
1063
        0,
1064
        0,
1065
        0,
1066
        0,
1067
        0,
1068
        1
1069
      ], value));
1070
    }
1071
    function adjustGrayscale(matrix, value) {
1072
      value = clamp(value, 0, 1);
1073
      return multiply(matrix, adjust([
1074
        0.33,
1075
        0.34,
1076
        0.33,
1077
        0,
1078
        0,
1079
        0.33,
1080
        0.34,
1081
        0.33,
1082
        0,
1083
        0,
1084
        0.33,
1085
        0.34,
1086
        0.33,
1087
        0,
1088
        0,
1089
        0,
1090
        0,
1091
        0,
1092
        1,
1093
        0,
1094
        0,
1095
        0,
1096
        0,
1097
        0,
1098
        1
1099
      ], value));
1100
    }
1101
    var ColorMatrix = {
1102
      identity: identity$1,
1103
      adjust: adjust,
1104
      multiply: multiply,
1105
      adjustContrast: adjustContrast,
1106
      adjustBrightness: adjustBrightness,
1107
      adjustSaturation: adjustSaturation,
1108
      adjustHue: adjustHue,
1109
      adjustColors: adjustColors,
1110
      adjustSepia: adjustSepia,
1111
      adjustGrayscale: adjustGrayscale
1112
    };
1113
1114
    function colorFilter(ir, matrix) {
1115
      return ir.toCanvas().then(function (canvas) {
1116
        return applyColorFilter(canvas, ir.getType(), matrix);
1117
      });
1118
    }
1119
    function applyColorFilter(canvas, type, matrix) {
1120
      var context = Canvas.get2dContext(canvas);
1121
      var pixels;
1122
      function applyMatrix(pixels, m) {
1123
        var d = pixels.data, r, g, b, a, i, m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7], m8 = m[8], m9 = m[9], m10 = m[10], m11 = m[11], m12 = m[12], m13 = m[13], m14 = m[14], m15 = m[15], m16 = m[16], m17 = m[17], m18 = m[18], m19 = m[19];
1124
        for (i = 0; i < d.length; i += 4) {
1125
          r = d[i];
1126
          g = d[i + 1];
1127
          b = d[i + 2];
1128
          a = d[i + 3];
1129
          d[i] = r * m0 + g * m1 + b * m2 + a * m3 + m4;
1130
          d[i + 1] = r * m5 + g * m6 + b * m7 + a * m8 + m9;
1131
          d[i + 2] = r * m10 + g * m11 + b * m12 + a * m13 + m14;
1132
          d[i + 3] = r * m15 + g * m16 + b * m17 + a * m18 + m19;
1133
        }
1134
        return pixels;
1135
      }
1136
      pixels = applyMatrix(context.getImageData(0, 0, canvas.width, canvas.height), matrix);
1137
      context.putImageData(pixels, 0, 0);
1138
      return ImageResult.fromCanvas(canvas, type);
1139
    }
1140
    function convoluteFilter(ir, matrix) {
1141
      return ir.toCanvas().then(function (canvas) {
1142
        return applyConvoluteFilter(canvas, ir.getType(), matrix);
1143
      });
1144
    }
1145
    function applyConvoluteFilter(canvas, type, matrix) {
1146
      var context = Canvas.get2dContext(canvas);
1147
      var pixelsIn, pixelsOut;
1148
      function applyMatrix(pixelsIn, pixelsOut, matrix) {
1149
        var rgba, drgba, side, halfSide, x, y, r, g, b, cx, cy, scx, scy, offset, wt, w, h;
1150
        function clamp(value, min, max) {
1151
          if (value > max) {
1152
            value = max;
1153
          } else if (value < min) {
1154
            value = min;
1155
          }
1156
          return value;
1157
        }
1158
        side = Math.round(Math.sqrt(matrix.length));
1159
        halfSide = Math.floor(side / 2);
1160
        rgba = pixelsIn.data;
1161
        drgba = pixelsOut.data;
1162
        w = pixelsIn.width;
1163
        h = pixelsIn.height;
1164
        for (y = 0; y < h; y++) {
1165
          for (x = 0; x < w; x++) {
1166
            r = g = b = 0;
1167
            for (cy = 0; cy < side; cy++) {
1168
              for (cx = 0; cx < side; cx++) {
1169
                scx = clamp(x + cx - halfSide, 0, w - 1);
1170
                scy = clamp(y + cy - halfSide, 0, h - 1);
1171
                offset = (scy * w + scx) * 4;
1172
                wt = matrix[cy * side + cx];
1173
                r += rgba[offset] * wt;
1174
                g += rgba[offset + 1] * wt;
1175
                b += rgba[offset + 2] * wt;
1176
              }
1177
            }
1178
            offset = (y * w + x) * 4;
1179
            drgba[offset] = clamp(r, 0, 255);
1180
            drgba[offset + 1] = clamp(g, 0, 255);
1181
            drgba[offset + 2] = clamp(b, 0, 255);
1182
          }
1183
        }
1184
        return pixelsOut;
1185
      }
1186
      pixelsIn = context.getImageData(0, 0, canvas.width, canvas.height);
1187
      pixelsOut = context.getImageData(0, 0, canvas.width, canvas.height);
1188
      pixelsOut = applyMatrix(pixelsIn, pixelsOut, matrix);
1189
      context.putImageData(pixelsOut, 0, 0);
1190
      return ImageResult.fromCanvas(canvas, type);
1191
    }
1192
    function functionColorFilter(colorFn) {
1193
      var filterImpl = function (canvas, type, value) {
1194
        var context = Canvas.get2dContext(canvas);
1195
        var pixels, i, lookup = new Array(256);
1196
        function applyLookup(pixels, lookup) {
1197
          var d = pixels.data, i;
1198
          for (i = 0; i < d.length; i += 4) {
1199
            d[i] = lookup[d[i]];
1200
            d[i + 1] = lookup[d[i + 1]];
1201
            d[i + 2] = lookup[d[i + 2]];
1202
          }
1203
          return pixels;
1204
        }
1205
        for (i = 0; i < lookup.length; i++) {
1206
          lookup[i] = colorFn(i, value);
1207
        }
1208
        pixels = applyLookup(context.getImageData(0, 0, canvas.width, canvas.height), lookup);
1209
        context.putImageData(pixels, 0, 0);
1210
        return ImageResult.fromCanvas(canvas, type);
1211
      };
1212
      return function (ir, value) {
1213
        return ir.toCanvas().then(function (canvas) {
1214
          return filterImpl(canvas, ir.getType(), value);
1215
        });
1216
      };
1217
    }
1218
    function complexAdjustableColorFilter(matrixAdjustFn) {
1219
      return function (ir, adjust) {
1220
        return colorFilter(ir, matrixAdjustFn(ColorMatrix.identity(), adjust));
1221
      };
1222
    }
1223
    function basicColorFilter(matrix) {
1224
      return function (ir) {
1225
        return colorFilter(ir, matrix);
1226
      };
1227
    }
1228
    function basicConvolutionFilter(kernel) {
1229
      return function (ir) {
1230
        return convoluteFilter(ir, kernel);
1231
      };
1232
    }
1233
    var Filters = {
1234
      invert: basicColorFilter([
1235
        -1,
1236
        0,
1237
        0,
1238
        0,
1239
        255,
1240
        0,
1241
        -1,
1242
        0,
1243
        0,
1244
        255,
1245
        0,
1246
        0,
1247
        -1,
1248
        0,
1249
        255,
1250
        0,
1251
        0,
1252
        0,
1253
        1,
1254
        0
1255
      ]),
1256
      brightness: complexAdjustableColorFilter(ColorMatrix.adjustBrightness),
1257
      hue: complexAdjustableColorFilter(ColorMatrix.adjustHue),
1258
      saturate: complexAdjustableColorFilter(ColorMatrix.adjustSaturation),
1259
      contrast: complexAdjustableColorFilter(ColorMatrix.adjustContrast),
1260
      grayscale: complexAdjustableColorFilter(ColorMatrix.adjustGrayscale),
1261
      sepia: complexAdjustableColorFilter(ColorMatrix.adjustSepia),
1262
      colorize: function (ir, adjustR, adjustG, adjustB) {
1263
        return colorFilter(ir, ColorMatrix.adjustColors(ColorMatrix.identity(), adjustR, adjustG, adjustB));
1264
      },
1265
      sharpen: basicConvolutionFilter([
1266
        0,
1267
        -1,
1268
        0,
1269
        -1,
1270
        5,
1271
        -1,
1272
        0,
1273
        -1,
1274
        0
1275
      ]),
1276
      emboss: basicConvolutionFilter([
1277
        -2,
1278
        -1,
1279
        0,
1280
        -1,
1281
        1,
1282
        1,
1283
        0,
1284
        1,
1285
        2
1286
      ]),
1287
      gamma: functionColorFilter(function (color, value) {
1288
        return Math.pow(color / 255, 1 - value) * 255;
1289
      }),
1290
      exposure: functionColorFilter(function (color, value) {
1291
        return 255 * (1 - Math.exp(-(color / 255) * value));
1292
      }),
1293
      colorFilter: colorFilter,
1294
      convoluteFilter: convoluteFilter
1295
    };
1296
1297
    function scale(image, dW, dH) {
1298
      var sW = ImageSize.getWidth(image);
1299
      var sH = ImageSize.getHeight(image);
1300
      var wRatio = dW / sW;
1301
      var hRatio = dH / sH;
1302
      var scaleCapped = false;
1303
      if (wRatio < 0.5 || wRatio > 2) {
1304
        wRatio = wRatio < 0.5 ? 0.5 : 2;
1305
        scaleCapped = true;
1306
      }
1307
      if (hRatio < 0.5 || hRatio > 2) {
1308
        hRatio = hRatio < 0.5 ? 0.5 : 2;
1309
        scaleCapped = true;
1310
      }
1311
      var scaled = _scale(image, wRatio, hRatio);
1312
      return !scaleCapped ? scaled : scaled.then(function (tCanvas) {
1313
        return scale(tCanvas, dW, dH);
1314
      });
1315
    }
1316
    function _scale(image, wRatio, hRatio) {
1317
      return new Promise(function (resolve) {
1318
        var sW = ImageSize.getWidth(image);
1319
        var sH = ImageSize.getHeight(image);
1320
        var dW = Math.floor(sW * wRatio);
1321
        var dH = Math.floor(sH * hRatio);
1322
        var canvas = Canvas.create(dW, dH);
1323
        var context = Canvas.get2dContext(canvas);
1324
        context.drawImage(image, 0, 0, sW, sH, 0, 0, dW, dH);
1325
        resolve(canvas);
1326
      });
1327
    }
1328
    var ImageResizerCanvas = { scale: scale };
1329
1330
    function rotate(ir, angle) {
1331
      return ir.toCanvas().then(function (canvas) {
1332
        return applyRotate(canvas, ir.getType(), angle);
1333
      });
1334
    }
1335
    function applyRotate(image, type, angle) {
1336
      var canvas = Canvas.create(image.width, image.height);
1337
      var context = Canvas.get2dContext(canvas);
1338
      var translateX = 0, translateY = 0;
1339
      angle = angle < 0 ? 360 + angle : angle;
1340
      if (angle == 90 || angle == 270) {
1341
        Canvas.resize(canvas, canvas.height, canvas.width);
1342
      }
1343
      if (angle == 90 || angle == 180) {
1344
        translateX = canvas.width;
1345
      }
1346
      if (angle == 270 || angle == 180) {
1347
        translateY = canvas.height;
1348
      }
1349
      context.translate(translateX, translateY);
1350
      context.rotate(angle * Math.PI / 180);
1351
      context.drawImage(image, 0, 0);
1352
      return ImageResult.fromCanvas(canvas, type);
1353
    }
1354
    function flip(ir, axis) {
1355
      return ir.toCanvas().then(function (canvas) {
1356
        return applyFlip(canvas, ir.getType(), axis);
1357
      });
1358
    }
1359
    function applyFlip(image, type, axis) {
1360
      var canvas = Canvas.create(image.width, image.height);
1361
      var context = Canvas.get2dContext(canvas);
1362
      if (axis == 'v') {
1363
        context.scale(1, -1);
1364
        context.drawImage(image, 0, -canvas.height);
1365
      } else {
1366
        context.scale(-1, 1);
1367
        context.drawImage(image, -canvas.width, 0);
1368
      }
1369
      return ImageResult.fromCanvas(canvas, type);
1370
    }
1371
    function crop(ir, x, y, w, h) {
1372
      return ir.toCanvas().then(function (canvas) {
1373
        return applyCrop(canvas, ir.getType(), x, y, w, h);
1374
      });
1375
    }
1376
    function applyCrop(image, type, x, y, w, h) {
1377
      var canvas = Canvas.create(w, h);
1378
      var context = Canvas.get2dContext(canvas);
1379
      context.drawImage(image, -x, -y);
1380
      return ImageResult.fromCanvas(canvas, type);
1381
    }
1382
    function resize$1(ir, w, h) {
1383
      return ir.toCanvas().then(function (canvas) {
1384
        return ImageResizerCanvas.scale(canvas, w, h).then(function (newCanvas) {
1385
          return ImageResult.fromCanvas(newCanvas, ir.getType());
1386
        });
1387
      });
1388
    }
1389
    var ImageTools = {
1390
      rotate: rotate,
1391
      flip: flip,
1392
      crop: crop,
1393
      resize: resize$1
1394
    };
1395
1396
    var BinaryReader = function () {
1397
      function BinaryReader(ar) {
1398
        this.littleEndian = false;
1399
        this._dv = new DataView(ar);
1400
      }
1401
      BinaryReader.prototype.readByteAt = function (idx) {
1402
        return this._dv.getUint8(idx);
1403
      };
1404
      BinaryReader.prototype.read = function (idx, size) {
1405
        if (idx + size > this.length()) {
1406
          return null;
1407
        }
1408
        var mv = this.littleEndian ? 0 : -8 * (size - 1);
1409
        for (var i = 0, sum = 0; i < size; i++) {
1410
          sum |= this.readByteAt(idx + i) << Math.abs(mv + i * 8);
1411
        }
1412
        return sum;
1413
      };
1414
      BinaryReader.prototype.BYTE = function (idx) {
1415
        return this.read(idx, 1);
1416
      };
1417
      BinaryReader.prototype.SHORT = function (idx) {
1418
        return this.read(idx, 2);
1419
      };
1420
      BinaryReader.prototype.LONG = function (idx) {
1421
        return this.read(idx, 4);
1422
      };
1423
      BinaryReader.prototype.SLONG = function (idx) {
1424
        var num = this.read(idx, 4);
1425
        return num > 2147483647 ? num - 4294967296 : num;
1426
      };
1427
      BinaryReader.prototype.CHAR = function (idx) {
1428
        return String.fromCharCode(this.read(idx, 1));
1429
      };
1430
      BinaryReader.prototype.STRING = function (idx, count) {
1431
        return this.asArray('CHAR', idx, count).join('');
1432
      };
1433
      BinaryReader.prototype.SEGMENT = function (idx, size) {
1434
        var ar = this._dv.buffer;
1435
        switch (arguments.length) {
1436
        case 2:
1437
          return ar.slice(idx, idx + size);
1438
        case 1:
1439
          return ar.slice(idx);
1440
        default:
1441
          return ar;
1442
        }
1443
      };
1444
      BinaryReader.prototype.asArray = function (type, idx, count) {
1445
        var values = [];
1446
        for (var i = 0; i < count; i++) {
1447
          values[i] = this[type](idx + i);
1448
        }
1449
        return values;
1450
      };
1451
      BinaryReader.prototype.length = function () {
1452
        return this._dv ? this._dv.byteLength : 0;
1453
      };
1454
      return BinaryReader;
1455
    }();
1456
1457
    var tags = {
1458
      tiff: {
1459
        274: 'Orientation',
1460
        270: 'ImageDescription',
1461
        271: 'Make',
1462
        272: 'Model',
1463
        305: 'Software',
1464
        34665: 'ExifIFDPointer',
1465
        34853: 'GPSInfoIFDPointer'
1466
      },
1467
      exif: {
1468
        36864: 'ExifVersion',
1469
        40961: 'ColorSpace',
1470
        40962: 'PixelXDimension',
1471
        40963: 'PixelYDimension',
1472
        36867: 'DateTimeOriginal',
1473
        33434: 'ExposureTime',
1474
        33437: 'FNumber',
1475
        34855: 'ISOSpeedRatings',
1476
        37377: 'ShutterSpeedValue',
1477
        37378: 'ApertureValue',
1478
        37383: 'MeteringMode',
1479
        37384: 'LightSource',
1480
        37385: 'Flash',
1481
        37386: 'FocalLength',
1482
        41986: 'ExposureMode',
1483
        41987: 'WhiteBalance',
1484
        41990: 'SceneCaptureType',
1485
        41988: 'DigitalZoomRatio',
1486
        41992: 'Contrast',
1487
        41993: 'Saturation',
1488
        41994: 'Sharpness'
1489
      },
1490
      gps: {
1491
        0: 'GPSVersionID',
1492
        1: 'GPSLatitudeRef',
1493
        2: 'GPSLatitude',
1494
        3: 'GPSLongitudeRef',
1495
        4: 'GPSLongitude'
1496
      },
1497
      thumb: {
1498
        513: 'JPEGInterchangeFormat',
1499
        514: 'JPEGInterchangeFormatLength'
1500
      }
1501
    };
1502
    var tagDescs = {
1503
      'ColorSpace': {
1504
        1: 'sRGB',
1505
        0: 'Uncalibrated'
1506
      },
1507
      'MeteringMode': {
1508
        0: 'Unknown',
1509
        1: 'Average',
1510
        2: 'CenterWeightedAverage',
1511
        3: 'Spot',
1512
        4: 'MultiSpot',
1513
        5: 'Pattern',
1514
        6: 'Partial',
1515
        255: 'Other'
1516
      },
1517
      'LightSource': {
1518
        1: 'Daylight',
1519
        2: 'Fliorescent',
1520
        3: 'Tungsten',
1521
        4: 'Flash',
1522
        9: 'Fine weather',
1523
        10: 'Cloudy weather',
1524
        11: 'Shade',
1525
        12: 'Daylight fluorescent (D 5700 - 7100K)',
1526
        13: 'Day white fluorescent (N 4600 -5400K)',
1527
        14: 'Cool white fluorescent (W 3900 - 4500K)',
1528
        15: 'White fluorescent (WW 3200 - 3700K)',
1529
        17: 'Standard light A',
1530
        18: 'Standard light B',
1531
        19: 'Standard light C',
1532
        20: 'D55',
1533
        21: 'D65',
1534
        22: 'D75',
1535
        23: 'D50',
1536
        24: 'ISO studio tungsten',
1537
        255: 'Other'
1538
      },
1539
      'Flash': {
1540
        0: 'Flash did not fire',
1541
        1: 'Flash fired',
1542
        5: 'Strobe return light not detected',
1543
        7: 'Strobe return light detected',
1544
        9: 'Flash fired, compulsory flash mode',
1545
        13: 'Flash fired, compulsory flash mode, return light not detected',
1546
        15: 'Flash fired, compulsory flash mode, return light detected',
1547
        16: 'Flash did not fire, compulsory flash mode',
1548
        24: 'Flash did not fire, auto mode',
1549
        25: 'Flash fired, auto mode',
1550
        29: 'Flash fired, auto mode, return light not detected',
1551
        31: 'Flash fired, auto mode, return light detected',
1552
        32: 'No flash function',
1553
        65: 'Flash fired, red-eye reduction mode',
1554
        69: 'Flash fired, red-eye reduction mode, return light not detected',
1555
        71: 'Flash fired, red-eye reduction mode, return light detected',
1556
        73: 'Flash fired, compulsory flash mode, red-eye reduction mode',
1557
        77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
1558
        79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
1559
        89: 'Flash fired, auto mode, red-eye reduction mode',
1560
        93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
1561
        95: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
1562
      },
1563
      'ExposureMode': {
1564
        0: 'Auto exposure',
1565
        1: 'Manual exposure',
1566
        2: 'Auto bracket'
1567
      },
1568
      'WhiteBalance': {
1569
        0: 'Auto white balance',
1570
        1: 'Manual white balance'
1571
      },
1572
      'SceneCaptureType': {
1573
        0: 'Standard',
1574
        1: 'Landscape',
1575
        2: 'Portrait',
1576
        3: 'Night scene'
1577
      },
1578
      'Contrast': {
1579
        0: 'Normal',
1580
        1: 'Soft',
1581
        2: 'Hard'
1582
      },
1583
      'Saturation': {
1584
        0: 'Normal',
1585
        1: 'Low saturation',
1586
        2: 'High saturation'
1587
      },
1588
      'Sharpness': {
1589
        0: 'Normal',
1590
        1: 'Soft',
1591
        2: 'Hard'
1592
      },
1593
      'GPSLatitudeRef': {
1594
        N: 'North latitude',
1595
        S: 'South latitude'
1596
      },
1597
      'GPSLongitudeRef': {
1598
        E: 'East longitude',
1599
        W: 'West longitude'
1600
      }
1601
    };
1602
    var ExifReader = function () {
1603
      function ExifReader(ar) {
1604
        this._offsets = {
1605
          tiffHeader: 10,
1606
          IFD0: null,
1607
          IFD1: null,
1608
          exifIFD: null,
1609
          gpsIFD: null
1610
        };
1611
        this._tiffTags = {};
1612
        var self = this;
1613
        self._reader = new BinaryReader(ar);
1614
        self._idx = self._offsets.tiffHeader;
1615
        if (self.SHORT(0) !== 65505 || self.STRING(4, 5).toUpperCase() !== 'EXIF\0') {
1616
          throw new Error('Exif data cannot be read or not available.');
1617
        }
1618
        self._reader.littleEndian = self.SHORT(self._idx) == 18761;
1619
        if (self.SHORT(self._idx += 2) !== 42) {
1620
          throw new Error('Invalid Exif data.');
1621
        }
1622
        self._offsets.IFD0 = self._offsets.tiffHeader + self.LONG(self._idx += 2);
1623
        self._tiffTags = self.extractTags(self._offsets.IFD0, tags.tiff);
1624
        if ('ExifIFDPointer' in self._tiffTags) {
1625
          self._offsets.exifIFD = self._offsets.tiffHeader + self._tiffTags.ExifIFDPointer;
1626
          delete self._tiffTags.ExifIFDPointer;
1627
        }
1628
        if ('GPSInfoIFDPointer' in self._tiffTags) {
1629
          self._offsets.gpsIFD = self._offsets.tiffHeader + self._tiffTags.GPSInfoIFDPointer;
1630
          delete self._tiffTags.GPSInfoIFDPointer;
1631
        }
1632
        var IFD1Offset = self.LONG(self._offsets.IFD0 + self.SHORT(self._offsets.IFD0) * 12 + 2);
1633
        if (IFD1Offset) {
1634
          self._offsets.IFD1 = self._offsets.tiffHeader + IFD1Offset;
1635
        }
1636
      }
1637
      ExifReader.prototype.BYTE = function (idx) {
1638
        return this._reader.BYTE(idx);
1639
      };
1640
      ExifReader.prototype.SHORT = function (idx) {
1641
        return this._reader.SHORT(idx);
1642
      };
1643
      ExifReader.prototype.LONG = function (idx) {
1644
        return this._reader.LONG(idx);
1645
      };
1646
      ExifReader.prototype.SLONG = function (idx) {
1647
        return this._reader.SLONG(idx);
1648
      };
1649
      ExifReader.prototype.CHAR = function (idx) {
1650
        return this._reader.CHAR(idx);
1651
      };
1652
      ExifReader.prototype.STRING = function (idx, count) {
1653
        return this._reader.STRING(idx, count);
1654
      };
1655
      ExifReader.prototype.SEGMENT = function (idx, size) {
1656
        return this._reader.SEGMENT(idx, size);
1657
      };
1658
      ExifReader.prototype.asArray = function (type, idx, count) {
1659
        var values = [];
1660
        for (var i = 0; i < count; i++) {
1661
          values[i] = this[type](idx + i);
1662
        }
1663
        return values;
1664
      };
1665
      ExifReader.prototype.length = function () {
1666
        return this._reader.length();
1667
      };
1668
      ExifReader.prototype.UNDEFINED = function () {
1669
        return this.BYTE.apply(this, arguments);
1670
      };
1671
      ExifReader.prototype.RATIONAL = function (idx) {
1672
        return this.LONG(idx) / this.LONG(idx + 4);
1673
      };
1674
      ExifReader.prototype.SRATIONAL = function (idx) {
1675
        return this.SLONG(idx) / this.SLONG(idx + 4);
1676
      };
1677
      ExifReader.prototype.ASCII = function (idx) {
1678
        return this.CHAR(idx);
1679
      };
1680
      ExifReader.prototype.TIFF = function () {
1681
        return this._tiffTags;
1682
      };
1683
      ExifReader.prototype.EXIF = function () {
1684
        var self = this;
1685
        var Exif = null;
1686
        if (self._offsets.exifIFD) {
1687
          try {
1688
            Exif = self.extractTags(self._offsets.exifIFD, tags.exif);
1689
          } catch (ex) {
1690
            return null;
1691
          }
1692
          if (Exif.ExifVersion && Array.isArray(Exif.ExifVersion)) {
1693
            for (var i = 0, exifVersion = ''; i < Exif.ExifVersion.length; i++) {
1694
              exifVersion += String.fromCharCode(Exif.ExifVersion[i]);
1695
            }
1696
            Exif.ExifVersion = exifVersion;
1697
          }
1698
        }
1699
        return Exif;
1700
      };
1701
      ExifReader.prototype.GPS = function () {
1702
        var self = this;
1703
        var GPS = null;
1704
        if (self._offsets.gpsIFD) {
1705
          try {
1706
            GPS = self.extractTags(self._offsets.gpsIFD, tags.gps);
1707
          } catch (ex) {
1708
            return null;
1709
          }
1710
          if (GPS.GPSVersionID && Array.isArray(GPS.GPSVersionID)) {
1711
            GPS.GPSVersionID = GPS.GPSVersionID.join('.');
1712
          }
1713
        }
1714
        return GPS;
1715
      };
1716
      ExifReader.prototype.thumb = function () {
1717
        var self = this;
1718
        if (self._offsets.IFD1) {
1719
          try {
1720
            var IFD1Tags = self.extractTags(self._offsets.IFD1, tags.thumb);
1721
            if ('JPEGInterchangeFormat' in IFD1Tags) {
1722
              return self.SEGMENT(self._offsets.tiffHeader + IFD1Tags.JPEGInterchangeFormat, IFD1Tags.JPEGInterchangeFormatLength);
1723
            }
1724
          } catch (ex) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
1725
          }
1726
        }
1727
        return null;
1728
      };
1729
      ExifReader.prototype.extractTags = function (IFD_offset, tags2extract) {
1730
        var self = this;
1731
        var length, i, tag, type, count, size, offset, value, values = [], hash = {};
0 ignored issues
show
Unused Code introduced by
The assignment to variable values seems to be never used. Consider removing it.
Loading history...
1732
        var types = {
1733
          1: 'BYTE',
1734
          7: 'UNDEFINED',
1735
          2: 'ASCII',
1736
          3: 'SHORT',
1737
          4: 'LONG',
1738
          5: 'RATIONAL',
1739
          9: 'SLONG',
1740
          10: 'SRATIONAL'
1741
        };
1742
        var sizes = {
1743
          'BYTE': 1,
1744
          'UNDEFINED': 1,
1745
          'ASCII': 1,
1746
          'SHORT': 2,
1747
          'LONG': 4,
1748
          'RATIONAL': 8,
1749
          'SLONG': 4,
1750
          'SRATIONAL': 8
1751
        };
1752
        length = self.SHORT(IFD_offset);
1753
        for (i = 0; i < length; i++) {
1754
          values = [];
1755
          offset = IFD_offset + 2 + i * 12;
1756
          tag = tags2extract[self.SHORT(offset)];
1757
          if (tag === undefined) {
1758
            continue;
1759
          }
1760
          type = types[self.SHORT(offset += 2)];
1761
          count = self.LONG(offset += 2);
1762
          size = sizes[type];
1763
          if (!size) {
1764
            throw new Error('Invalid Exif data.');
1765
          }
1766
          offset += 4;
1767
          if (size * count > 4) {
1768
            offset = self.LONG(offset) + self._offsets.tiffHeader;
1769
          }
1770
          if (offset + size * count >= self.length()) {
1771
            throw new Error('Invalid Exif data.');
1772
          }
1773
          if (type === 'ASCII') {
1774
            hash[tag] = self.STRING(offset, count).replace(/\0$/, '').trim();
1775
            continue;
0 ignored issues
show
Unused Code introduced by
This continue has no effect on the loop flow and can be removed.
Loading history...
1776
          } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
1777
            values = self.asArray(type, offset, count);
1778
            value = count == 1 ? values[0] : values;
0 ignored issues
show
Best Practice introduced by
Comparing count to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1779
            if (tagDescs.hasOwnProperty(tag) && typeof value != 'object') {
1780
              hash[tag] = tagDescs[tag][value];
1781
            } else {
1782
              hash[tag] = value;
1783
            }
1784
          }
1785
        }
1786
        return hash;
1787
      };
1788
      return ExifReader;
1789
    }();
1790
1791
    var extractFrom = function (blob) {
1792
      return Conversions.blobToArrayBuffer(blob).then(function (ar) {
1793
        try {
1794
          var br = new BinaryReader(ar);
1795
          if (br.SHORT(0) === 65496) {
1796
            var headers = extractHeaders(br);
1797
            var app1 = headers.filter(function (header) {
1798
              return header.name === 'APP1';
1799
            });
1800
            var meta = {};
0 ignored issues
show
Unused Code introduced by
The assignment to variable meta seems to be never used. Consider removing it.
Loading history...
1801
            if (app1.length) {
1802
              var exifReader = new ExifReader(app1[0].segment);
1803
              meta = {
1804
                tiff: exifReader.TIFF(),
1805
                exif: exifReader.EXIF(),
1806
                gps: exifReader.GPS(),
1807
                thumb: exifReader.thumb()
1808
              };
1809
            } else {
1810
              return Promise.reject('Headers did not include required information');
1811
            }
1812
            meta.rawHeaders = headers;
1813
            return meta;
1814
          }
1815
          return Promise.reject('Image was not a jpeg');
1816
        } catch (ex) {
1817
          return Promise.reject('Unsupported format or not an image: ' + blob.type + ' (Exception: ' + ex.message + ')');
1818
        }
1819
      });
1820
    };
1821
    var extractHeaders = function (br) {
1822
      var headers = [], idx, marker, length = 0;
1823
      idx = 2;
1824
      while (idx <= br.length()) {
1825
        marker = br.SHORT(idx);
1826
        if (marker >= 65488 && marker <= 65495) {
1827
          idx += 2;
1828
          continue;
1829
        }
1830
        if (marker === 65498 || marker === 65497) {
1831
          break;
1832
        }
1833
        length = br.SHORT(idx + 2) + 2;
1834
        if (marker >= 65505 && marker <= 65519) {
1835
          headers.push({
1836
            hex: marker,
1837
            name: 'APP' + (marker & 15),
1838
            start: idx,
1839
            length: length,
1840
            segment: br.SEGMENT(idx, length)
1841
          });
1842
        }
1843
        idx += length;
1844
      }
1845
      return headers;
1846
    };
1847
    var JPEGMeta = { extractFrom: extractFrom };
1848
1849
    var invert = function (ir) {
1850
      return Filters.invert(ir);
1851
    };
1852
    var sharpen = function (ir) {
1853
      return Filters.sharpen(ir);
1854
    };
1855
    var emboss = function (ir) {
1856
      return Filters.emboss(ir);
1857
    };
1858
    var gamma = function (ir, value) {
1859
      return Filters.gamma(ir, value);
1860
    };
1861
    var exposure = function (ir, value) {
1862
      return Filters.exposure(ir, value);
1863
    };
1864
    var colorize = function (ir, adjustR, adjustG, adjustB) {
1865
      return Filters.colorize(ir, adjustR, adjustG, adjustB);
1866
    };
1867
    var brightness = function (ir, adjust) {
1868
      return Filters.brightness(ir, adjust);
1869
    };
1870
    var hue = function (ir, adjust) {
1871
      return Filters.hue(ir, adjust);
1872
    };
1873
    var saturate = function (ir, adjust) {
1874
      return Filters.saturate(ir, adjust);
1875
    };
1876
    var contrast = function (ir, adjust) {
1877
      return Filters.contrast(ir, adjust);
1878
    };
1879
    var grayscale = function (ir, adjust) {
1880
      return Filters.grayscale(ir, adjust);
1881
    };
1882
    var sepia = function (ir, adjust) {
1883
      return Filters.sepia(ir, adjust);
1884
    };
1885
    var flip$1 = function (ir, axis) {
1886
      return ImageTools.flip(ir, axis);
1887
    };
1888
    var crop$1 = function (ir, x, y, w, h) {
1889
      return ImageTools.crop(ir, x, y, w, h);
1890
    };
1891
    var resize$2 = function (ir, w, h) {
1892
      return ImageTools.resize(ir, w, h);
1893
    };
1894
    var rotate$1 = function (ir, angle) {
1895
      return ImageTools.rotate(ir, angle);
1896
    };
1897
    var exifRotate = function (ir) {
1898
      var ROTATE_90 = 6;
1899
      var ROTATE_180 = 3;
1900
      var ROTATE_270 = 8;
1901
      var checkRotation = function (data) {
1902
        var orientation = data.tiff.Orientation;
1903
        switch (orientation) {
1904
        case ROTATE_90:
1905
          return rotate$1(ir, 90);
1906
        case ROTATE_180:
1907
          return rotate$1(ir, 180);
1908
        case ROTATE_270:
1909
          return rotate$1(ir, 270);
1910
        default:
1911
          return ir;
1912
        }
1913
      };
1914
      var notJpeg = function () {
1915
        return ir;
1916
      };
1917
      return ir.toBlob().then(JPEGMeta.extractFrom).then(checkRotation, notJpeg);
1918
    };
1919
    var ImageTransformations = {
1920
      invert: invert,
1921
      sharpen: sharpen,
1922
      emboss: emboss,
1923
      brightness: brightness,
1924
      hue: hue,
1925
      saturate: saturate,
1926
      contrast: contrast,
1927
      grayscale: grayscale,
1928
      sepia: sepia,
1929
      colorize: colorize,
1930
      gamma: gamma,
1931
      exposure: exposure,
1932
      flip: flip$1,
1933
      crop: crop$1,
1934
      resize: resize$2,
1935
      rotate: rotate$1,
1936
      exifRotate: exifRotate
1937
    };
1938
1939
    var blobToImageResult = function (blob) {
1940
      return ImageResult.fromBlob(blob);
1941
    };
1942
    var fromBlobAndUrlSync$1 = function (blob, uri) {
1943
      return ImageResult.fromBlobAndUrlSync(blob, uri);
1944
    };
1945
    var imageToImageResult = function (image) {
1946
      return ImageResult.fromImage(image);
1947
    };
1948
    var imageResultToBlob = function (ir, type, quality) {
1949
      if (type === undefined && quality === undefined) {
1950
        return imageResultToOriginalBlob(ir);
1951
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
1952
        return ir.toAdjustedBlob(type, quality);
1953
      }
1954
    };
1955
    var imageResultToOriginalBlob = function (ir) {
1956
      return ir.toBlob();
1957
    };
1958
    var imageResultToDataURL = function (ir) {
1959
      return ir.toDataURL();
1960
    };
1961
    var ResultConversions = {
1962
      blobToImageResult: blobToImageResult,
1963
      fromBlobAndUrlSync: fromBlobAndUrlSync$1,
1964
      imageToImageResult: imageToImageResult,
1965
      imageResultToBlob: imageResultToBlob,
1966
      imageResultToOriginalBlob: imageResultToOriginalBlob,
1967
      imageResultToDataURL: imageResultToDataURL
1968
    };
1969
1970
    var url = function () {
1971
      return Global$1.getOrDie('URL');
1972
    };
1973
    var createObjectURL = function (blob) {
1974
      return url().createObjectURL(blob);
1975
    };
1976
    var revokeObjectURL = function (u) {
1977
      url().revokeObjectURL(u);
1978
    };
1979
    var URL$1 = {
1980
      createObjectURL: createObjectURL,
1981
      revokeObjectURL: revokeObjectURL
1982
    };
1983
1984
    var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay');
1985
1986
    var global$3 = tinymce.util.Tools.resolve('tinymce.util.Promise');
1987
1988
    var global$4 = tinymce.util.Tools.resolve('tinymce.util.URI');
1989
1990
    var getToolbarItems = function (editor) {
1991
      return editor.getParam('imagetools_toolbar', 'rotateleft rotateright | flipv fliph | crop editimage imageoptions');
1992
    };
1993
    var getProxyUrl = function (editor) {
1994
      return editor.getParam('imagetools_proxy');
1995
    };
1996
    var getCorsHosts = function (editor) {
1997
      return editor.getParam('imagetools_cors_hosts', [], 'string[]');
1998
    };
1999
    var getCredentialsHosts = function (editor) {
2000
      return editor.getParam('imagetools_credentials_hosts', [], 'string[]');
2001
    };
2002
    var getApiKey = function (editor) {
2003
      return editor.getParam('api_key', editor.getParam('imagetools_api_key', '', 'string'), 'string');
2004
    };
2005
    var getUploadTimeout = function (editor) {
2006
      return editor.getParam('images_upload_timeout', 30000, 'number');
2007
    };
2008
    var shouldReuseFilename = function (editor) {
2009
      return editor.getParam('images_reuse_filename', false, 'boolean');
2010
    };
2011
2012
    var global$5 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
2013
2014
    var global$6 = tinymce.util.Tools.resolve('tinymce.ui.Factory');
2015
2016
    function UndoStack () {
2017
      var data = [];
2018
      var index = -1;
2019
      function add(state) {
2020
        var removed;
2021
        removed = data.splice(++index);
2022
        data.push(state);
2023
        return {
2024
          state: state,
2025
          removed: removed
2026
        };
2027
      }
2028
      function undo() {
2029
        if (canUndo()) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if canUndo() is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
2030
          return data[--index];
2031
        }
2032
      }
2033
      function redo() {
2034
        if (canRedo()) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if canRedo() is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
2035
          return data[++index];
2036
        }
2037
      }
2038
      function canUndo() {
2039
        return index > 0;
2040
      }
2041
      function canRedo() {
2042
        return index !== -1 && index < data.length - 1;
2043
      }
2044
      return {
2045
        data: data,
2046
        add: add,
2047
        undo: undo,
2048
        redo: redo,
2049
        canUndo: canUndo,
2050
        canRedo: canRedo
2051
      };
2052
    }
2053
2054
    var global$7 = tinymce.util.Tools.resolve('tinymce.geom.Rect');
2055
2056
    var loadImage$1 = function (image) {
2057
      return new global$3(function (resolve) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
2058
        var loaded = function () {
2059
          image.removeEventListener('load', loaded);
2060
          resolve(image);
2061
        };
2062
        if (image.complete) {
2063
          resolve(image);
2064
        } else {
2065
          image.addEventListener('load', loaded);
2066
        }
2067
      });
2068
    };
2069
    var LoadImage = { loadImage: loadImage$1 };
2070
2071
    var global$8 = tinymce.util.Tools.resolve('tinymce.dom.DomQuery');
2072
2073
    var global$9 = tinymce.util.Tools.resolve('tinymce.util.Observable');
2074
2075
    var global$a = tinymce.util.Tools.resolve('tinymce.util.VK');
2076
2077
    var count = 0;
2078
    function CropRect (currentRect, viewPortRect, clampRect, containerElm, action) {
2079
      var instance;
2080
      var handles;
2081
      var dragHelpers;
2082
      var blockers;
2083
      var prefix = 'mce-';
2084
      var id = prefix + 'crid-' + count++;
2085
      handles = [
2086
        {
2087
          name: 'move',
2088
          xMul: 0,
2089
          yMul: 0,
2090
          deltaX: 1,
2091
          deltaY: 1,
2092
          deltaW: 0,
2093
          deltaH: 0,
2094
          label: 'Crop Mask'
2095
        },
2096
        {
2097
          name: 'nw',
2098
          xMul: 0,
2099
          yMul: 0,
2100
          deltaX: 1,
2101
          deltaY: 1,
2102
          deltaW: -1,
2103
          deltaH: -1,
2104
          label: 'Top Left Crop Handle'
2105
        },
2106
        {
2107
          name: 'ne',
2108
          xMul: 1,
2109
          yMul: 0,
2110
          deltaX: 0,
2111
          deltaY: 1,
2112
          deltaW: 1,
2113
          deltaH: -1,
2114
          label: 'Top Right Crop Handle'
2115
        },
2116
        {
2117
          name: 'sw',
2118
          xMul: 0,
2119
          yMul: 1,
2120
          deltaX: 1,
2121
          deltaY: 0,
2122
          deltaW: -1,
2123
          deltaH: 1,
2124
          label: 'Bottom Left Crop Handle'
2125
        },
2126
        {
2127
          name: 'se',
2128
          xMul: 1,
2129
          yMul: 1,
2130
          deltaX: 0,
2131
          deltaY: 0,
2132
          deltaW: 1,
2133
          deltaH: 1,
2134
          label: 'Bottom Right Crop Handle'
2135
        }
2136
      ];
2137
      blockers = [
2138
        'top',
2139
        'right',
2140
        'bottom',
2141
        'left'
2142
      ];
2143
      function getAbsoluteRect(outerRect, relativeRect) {
2144
        return {
2145
          x: relativeRect.x + outerRect.x,
2146
          y: relativeRect.y + outerRect.y,
2147
          w: relativeRect.w,
2148
          h: relativeRect.h
2149
        };
2150
      }
2151
      function getRelativeRect(outerRect, innerRect) {
2152
        return {
2153
          x: innerRect.x - outerRect.x,
2154
          y: innerRect.y - outerRect.y,
2155
          w: innerRect.w,
2156
          h: innerRect.h
2157
        };
2158
      }
2159
      function getInnerRect() {
2160
        return getRelativeRect(clampRect, currentRect);
2161
      }
2162
      function moveRect(handle, startRect, deltaX, deltaY) {
2163
        var x, y, w, h, rect;
2164
        x = startRect.x;
2165
        y = startRect.y;
2166
        w = startRect.w;
2167
        h = startRect.h;
2168
        x += deltaX * handle.deltaX;
2169
        y += deltaY * handle.deltaY;
2170
        w += deltaX * handle.deltaW;
2171
        h += deltaY * handle.deltaH;
2172
        if (w < 20) {
2173
          w = 20;
2174
        }
2175
        if (h < 20) {
2176
          h = 20;
2177
        }
2178
        rect = currentRect = global$7.clamp({
2179
          x: x,
2180
          y: y,
2181
          w: w,
2182
          h: h
2183
        }, clampRect, handle.name === 'move');
2184
        rect = getRelativeRect(clampRect, rect);
2185
        instance.fire('updateRect', { rect: rect });
2186
        setInnerRect(rect);
2187
      }
2188
      function render() {
2189
        function createDragHelper(handle) {
2190
          var startRect;
2191
          var DragHelper = global$6.get('DragHelper');
2192
          return new DragHelper(id, {
2193
            document: containerElm.ownerDocument,
2194
            handle: id + '-' + handle.name,
2195
            start: function () {
2196
              startRect = currentRect;
2197
            },
2198
            drag: function (e) {
2199
              moveRect(handle, startRect, e.deltaX, e.deltaY);
2200
            }
2201
          });
2202
        }
2203
        global$8('<div id="' + id + '" class="' + prefix + 'croprect-container"' + ' role="grid" aria-dropeffect="execute">').appendTo(containerElm);
2204
        global$1.each(blockers, function (blocker) {
2205
          global$8('#' + id, containerElm).append('<div id="' + id + '-' + blocker + '"class="' + prefix + 'croprect-block" style="display: none" data-mce-bogus="all">');
2206
        });
2207
        global$1.each(handles, function (handle) {
2208
          global$8('#' + id, containerElm).append('<div id="' + id + '-' + handle.name + '" class="' + prefix + 'croprect-handle ' + prefix + 'croprect-handle-' + handle.name + '"' + 'style="display: none" data-mce-bogus="all" role="gridcell" tabindex="-1"' + ' aria-label="' + handle.label + '" aria-grabbed="false">');
2209
        });
2210
        dragHelpers = global$1.map(handles, createDragHelper);
2211
        repaint(currentRect);
2212
        global$8(containerElm).on('focusin focusout', function (e) {
2213
          global$8(e.target).attr('aria-grabbed', e.type === 'focus');
2214
        });
2215
        global$8(containerElm).on('keydown', function (e) {
2216
          var activeHandle;
2217
          global$1.each(handles, function (handle) {
2218
            if (e.target.id === id + '-' + handle.name) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if e.target.id === id + "-" + handle.name is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
2219
              activeHandle = handle;
2220
              return false;
2221
            }
2222
          });
2223
          function moveAndBlock(evt, handle, startRect, deltaX, deltaY) {
2224
            evt.stopPropagation();
2225
            evt.preventDefault();
2226
            moveRect(activeHandle, startRect, deltaX, deltaY);
2227
          }
2228
          switch (e.keyCode) {
2229
          case global$a.LEFT:
2230
            moveAndBlock(e, activeHandle, currentRect, -10, 0);
2231
            break;
2232
          case global$a.RIGHT:
2233
            moveAndBlock(e, activeHandle, currentRect, 10, 0);
2234
            break;
2235
          case global$a.UP:
2236
            moveAndBlock(e, activeHandle, currentRect, 0, -10);
2237
            break;
2238
          case global$a.DOWN:
2239
            moveAndBlock(e, activeHandle, currentRect, 0, 10);
2240
            break;
2241
          case global$a.ENTER:
2242
          case global$a.SPACEBAR:
2243
            e.preventDefault();
2244
            action();
2245
            break;
2246
          }
2247
        });
2248
      }
2249
      function toggleVisibility(state) {
2250
        var selectors;
2251
        selectors = global$1.map(handles, function (handle) {
2252
          return '#' + id + '-' + handle.name;
2253
        }).concat(global$1.map(blockers, function (blocker) {
2254
          return '#' + id + '-' + blocker;
2255
        })).join(',');
2256
        if (state) {
2257
          global$8(selectors, containerElm).show();
2258
        } else {
2259
          global$8(selectors, containerElm).hide();
2260
        }
2261
      }
2262
      function repaint(rect) {
2263
        function updateElementRect(name, rect) {
2264
          if (rect.h < 0) {
2265
            rect.h = 0;
2266
          }
2267
          if (rect.w < 0) {
2268
            rect.w = 0;
2269
          }
2270
          global$8('#' + id + '-' + name, containerElm).css({
2271
            left: rect.x,
2272
            top: rect.y,
2273
            width: rect.w,
2274
            height: rect.h
2275
          });
2276
        }
2277
        global$1.each(handles, function (handle) {
2278
          global$8('#' + id + '-' + handle.name, containerElm).css({
2279
            left: rect.w * handle.xMul + rect.x,
2280
            top: rect.h * handle.yMul + rect.y
2281
          });
2282
        });
2283
        updateElementRect('top', {
2284
          x: viewPortRect.x,
2285
          y: viewPortRect.y,
2286
          w: viewPortRect.w,
2287
          h: rect.y - viewPortRect.y
2288
        });
2289
        updateElementRect('right', {
2290
          x: rect.x + rect.w,
2291
          y: rect.y,
2292
          w: viewPortRect.w - rect.x - rect.w + viewPortRect.x,
2293
          h: rect.h
2294
        });
2295
        updateElementRect('bottom', {
2296
          x: viewPortRect.x,
2297
          y: rect.y + rect.h,
2298
          w: viewPortRect.w,
2299
          h: viewPortRect.h - rect.y - rect.h + viewPortRect.y
2300
        });
2301
        updateElementRect('left', {
2302
          x: viewPortRect.x,
2303
          y: rect.y,
2304
          w: rect.x - viewPortRect.x,
2305
          h: rect.h
2306
        });
2307
        updateElementRect('move', rect);
2308
      }
2309
      function setRect(rect) {
2310
        currentRect = rect;
2311
        repaint(currentRect);
2312
      }
2313
      function setViewPortRect(rect) {
2314
        viewPortRect = rect;
2315
        repaint(currentRect);
2316
      }
2317
      function setInnerRect(rect) {
2318
        setRect(getAbsoluteRect(clampRect, rect));
2319
      }
2320
      function setClampRect(rect) {
2321
        clampRect = rect;
2322
        repaint(currentRect);
2323
      }
2324
      function destroy() {
2325
        global$1.each(dragHelpers, function (helper) {
2326
          helper.destroy();
2327
        });
2328
        dragHelpers = [];
2329
      }
2330
      render();
2331
      instance = global$1.extend({
2332
        toggleVisibility: toggleVisibility,
2333
        setClampRect: setClampRect,
2334
        setRect: setRect,
2335
        getInnerRect: getInnerRect,
2336
        setInnerRect: setInnerRect,
2337
        setViewPortRect: setViewPortRect,
2338
        destroy: destroy
2339
      }, global$9);
2340
      return instance;
2341
    }
2342
2343
    var create$2 = function (settings) {
2344
      var Control = global$6.get('Control');
2345
      var ImagePanel = Control.extend({
2346
        Defaults: { classes: 'imagepanel' },
2347
        selection: function (rect) {
2348
          if (arguments.length) {
2349
            this.state.set('rect', rect);
2350
            return this;
2351
          }
2352
          return this.state.get('rect');
2353
        },
2354
        imageSize: function () {
2355
          var viewRect = this.state.get('viewRect');
2356
          return {
2357
            w: viewRect.w,
2358
            h: viewRect.h
2359
          };
2360
        },
2361
        toggleCropRect: function (state) {
2362
          this.state.set('cropEnabled', state);
2363
        },
2364
        imageSrc: function (url) {
2365
          var self$$1 = this, img = new Image();
0 ignored issues
show
Bug introduced by
The variable Image seems to be never declared. If this is a global, consider adding a /** global: Image */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2366
          img.src = url;
2367
          LoadImage.loadImage(img).then(function () {
2368
            var rect, $img;
2369
            var lastRect = self$$1.state.get('viewRect');
2370
            $img = self$$1.$el.find('img');
2371
            if ($img[0]) {
2372
              $img.replaceWith(img);
2373
            } else {
2374
              var bg = document.createElement('div');
2375
              bg.className = 'mce-imagepanel-bg';
2376
              self$$1.getEl().appendChild(bg);
2377
              self$$1.getEl().appendChild(img);
2378
            }
2379
            rect = {
2380
              x: 0,
2381
              y: 0,
2382
              w: img.naturalWidth,
2383
              h: img.naturalHeight
2384
            };
2385
            self$$1.state.set('viewRect', rect);
2386
            self$$1.state.set('rect', global$7.inflate(rect, -20, -20));
2387
            if (!lastRect || lastRect.w !== rect.w || lastRect.h !== rect.h) {
2388
              self$$1.zoomFit();
2389
            }
2390
            self$$1.repaintImage();
2391
            self$$1.fire('load');
2392
          });
2393
        },
2394
        zoom: function (value) {
2395
          if (arguments.length) {
2396
            this.state.set('zoom', value);
2397
            return this;
2398
          }
2399
          return this.state.get('zoom');
2400
        },
2401
        postRender: function () {
2402
          this.imageSrc(this.settings.imageSrc);
2403
          return this._super();
2404
        },
2405
        zoomFit: function () {
2406
          var self$$1 = this;
2407
          var $img, pw, ph, w, h, zoom, padding;
2408
          padding = 10;
2409
          $img = self$$1.$el.find('img');
2410
          pw = self$$1.getEl().clientWidth;
2411
          ph = self$$1.getEl().clientHeight;
2412
          w = $img[0].naturalWidth;
2413
          h = $img[0].naturalHeight;
2414
          zoom = Math.min((pw - padding) / w, (ph - padding) / h);
2415
          if (zoom >= 1) {
2416
            zoom = 1;
2417
          }
2418
          self$$1.zoom(zoom);
2419
        },
2420
        repaintImage: function () {
2421
          var x, y, w, h, pw, ph, $img, $bg, zoom, rect, elm;
2422
          elm = this.getEl();
2423
          zoom = this.zoom();
2424
          rect = this.state.get('rect');
2425
          $img = this.$el.find('img');
2426
          $bg = this.$el.find('.mce-imagepanel-bg');
2427
          pw = elm.offsetWidth;
2428
          ph = elm.offsetHeight;
2429
          w = $img[0].naturalWidth * zoom;
2430
          h = $img[0].naturalHeight * zoom;
2431
          x = Math.max(0, pw / 2 - w / 2);
2432
          y = Math.max(0, ph / 2 - h / 2);
2433
          $img.css({
2434
            left: x,
2435
            top: y,
2436
            width: w,
2437
            height: h
2438
          });
2439
          $bg.css({
2440
            left: x,
2441
            top: y,
2442
            width: w,
2443
            height: h
2444
          });
2445
          if (this.cropRect) {
2446
            this.cropRect.setRect({
2447
              x: rect.x * zoom + x,
2448
              y: rect.y * zoom + y,
2449
              w: rect.w * zoom,
2450
              h: rect.h * zoom
2451
            });
2452
            this.cropRect.setClampRect({
2453
              x: x,
2454
              y: y,
2455
              w: w,
2456
              h: h
2457
            });
2458
            this.cropRect.setViewPortRect({
2459
              x: 0,
2460
              y: 0,
2461
              w: pw,
2462
              h: ph
2463
            });
2464
          }
2465
        },
2466
        bindStates: function () {
2467
          var self$$1 = this;
2468
          function setupCropRect(rect) {
2469
            self$$1.cropRect = CropRect(rect, self$$1.state.get('viewRect'), self$$1.state.get('viewRect'), self$$1.getEl(), function () {
2470
              self$$1.fire('crop');
2471
            });
2472
            self$$1.cropRect.on('updateRect', function (e) {
2473
              var rect = e.rect;
2474
              var zoom = self$$1.zoom();
2475
              rect = {
2476
                x: Math.round(rect.x / zoom),
2477
                y: Math.round(rect.y / zoom),
2478
                w: Math.round(rect.w / zoom),
2479
                h: Math.round(rect.h / zoom)
2480
              };
2481
              self$$1.state.set('rect', rect);
2482
            });
2483
            self$$1.on('remove', self$$1.cropRect.destroy);
2484
          }
2485
          self$$1.state.on('change:cropEnabled', function (e) {
2486
            self$$1.cropRect.toggleVisibility(e.value);
2487
            self$$1.repaintImage();
2488
          });
2489
          self$$1.state.on('change:zoom', function () {
2490
            self$$1.repaintImage();
2491
          });
2492
          self$$1.state.on('change:rect', function (e) {
2493
            var rect = e.value;
2494
            if (!self$$1.cropRect) {
2495
              setupCropRect(rect);
2496
            }
2497
            self$$1.cropRect.setRect(rect);
2498
          });
2499
        }
2500
      });
2501
      return new ImagePanel(settings);
2502
    };
2503
    var ImagePanel = { create: create$2 };
2504
2505
    function createState(blob) {
2506
      return {
2507
        blob: blob,
2508
        url: URL$1.createObjectURL(blob)
2509
      };
2510
    }
2511
    function destroyState(state) {
2512
      if (state) {
2513
        URL$1.revokeObjectURL(state.url);
2514
      }
2515
    }
2516
    function destroyStates(states) {
2517
      global$1.each(states, destroyState);
2518
    }
2519
    function open(editor, currentState, resolve, reject) {
2520
      var win, undoStack = UndoStack(), mainPanel, filtersPanel, tempState, cropPanel, resizePanel, flipRotatePanel, imagePanel, sidePanel, mainViewContainer, invertPanel, brightnessPanel, huePanel, saturatePanel, contrastPanel, grayscalePanel, sepiaPanel, colorizePanel, sharpenPanel, embossPanel, gammaPanel, exposurePanel, panels, width, height, ratioW, ratioH;
2521
      var reverseIfRtl = function (items) {
2522
        return editor.rtl ? items.reverse() : items;
2523
      };
2524
      function recalcSize(e) {
2525
        var widthCtrl, heightCtrl, newWidth, newHeight;
2526
        widthCtrl = win.find('#w')[0];
2527
        heightCtrl = win.find('#h')[0];
2528
        newWidth = parseInt(widthCtrl.value(), 10);
2529
        newHeight = parseInt(heightCtrl.value(), 10);
2530
        if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
2531
          if (e.control.settings.name === 'w') {
2532
            newHeight = Math.round(newWidth * ratioW);
2533
            heightCtrl.value(newHeight);
2534
          } else {
2535
            newWidth = Math.round(newHeight * ratioH);
2536
            widthCtrl.value(newWidth);
2537
          }
2538
        }
2539
        width = newWidth;
2540
        height = newHeight;
2541
      }
2542
      function floatToPercent(value) {
2543
        return Math.round(value * 100) + '%';
2544
      }
2545
      function updateButtonUndoStates() {
2546
        win.find('#undo').disabled(!undoStack.canUndo());
2547
        win.find('#redo').disabled(!undoStack.canRedo());
2548
        win.statusbar.find('#save').disabled(!undoStack.canUndo());
2549
      }
2550
      function disableUndoRedo() {
2551
        win.find('#undo').disabled(true);
2552
        win.find('#redo').disabled(true);
2553
      }
2554
      function displayState(state) {
2555
        if (state) {
2556
          imagePanel.imageSrc(state.url);
2557
        }
2558
      }
2559
      function switchPanel(targetPanel) {
2560
        return function () {
2561
          var hidePanels = global$1.grep(panels, function (panel) {
2562
            return panel.settings.name !== targetPanel;
2563
          });
2564
          global$1.each(hidePanels, function (panel) {
2565
            panel.hide();
2566
          });
2567
          targetPanel.show();
2568
          targetPanel.focus();
2569
        };
2570
      }
2571
      function addTempState(blob) {
2572
        tempState = createState(blob);
2573
        displayState(tempState);
2574
      }
2575
      function addBlobState(blob) {
2576
        currentState = createState(blob);
2577
        displayState(currentState);
2578
        destroyStates(undoStack.add(currentState).removed);
2579
        updateButtonUndoStates();
2580
      }
2581
      function crop() {
2582
        var rect = imagePanel.selection();
2583
        ResultConversions.blobToImageResult(currentState.blob).then(function (ir) {
2584
          ImageTransformations.crop(ir, rect.x, rect.y, rect.w, rect.h).then(imageResultToBlob).then(function (blob) {
2585
            addBlobState(blob);
2586
            cancel();
2587
          });
2588
        });
2589
      }
2590
      var tempAction = function (fn) {
2591
        var args = [].slice.call(arguments, 1);
2592
        return function () {
2593
          var state = tempState || currentState;
2594
          ResultConversions.blobToImageResult(state.blob).then(function (ir) {
2595
            fn.apply(this, [ir].concat(args)).then(imageResultToBlob).then(addTempState);
2596
          });
2597
        };
2598
      };
2599
      function action(fn) {
2600
        var arg = [];
2601
        for (var _i = 1; _i < arguments.length; _i++) {
2602
          arg[_i - 1] = arguments[_i];
2603
        }
2604
        var args = [].slice.call(arguments, 1);
2605
        return function () {
2606
          ResultConversions.blobToImageResult(currentState.blob).then(function (ir) {
2607
            fn.apply(this, [ir].concat(args)).then(imageResultToBlob).then(addBlobState);
2608
          });
2609
        };
2610
      }
2611
      function cancel() {
2612
        displayState(currentState);
2613
        destroyState(tempState);
2614
        switchPanel(mainPanel)();
2615
        updateButtonUndoStates();
2616
      }
2617
      function waitForTempState(times, applyCall) {
2618
        if (tempState) {
2619
          applyCall();
2620
        } else {
2621
          setTimeout(function () {
2622
            if (times-- > 0) {
2623
              waitForTempState(times, applyCall);
2624
            } else {
2625
              editor.windowManager.alert('Error: failed to apply image operation.');
2626
            }
2627
          }, 10);
2628
        }
2629
      }
2630
      function applyTempState() {
2631
        if (tempState) {
2632
          addBlobState(tempState.blob);
2633
          cancel();
2634
        } else {
2635
          waitForTempState(100, applyTempState);
2636
        }
2637
      }
2638
      function zoomIn() {
2639
        var zoom = imagePanel.zoom();
2640
        if (zoom < 2) {
2641
          zoom += 0.1;
2642
        }
2643
        imagePanel.zoom(zoom);
2644
      }
2645
      function zoomOut() {
2646
        var zoom = imagePanel.zoom();
2647
        if (zoom > 0.1) {
2648
          zoom -= 0.1;
2649
        }
2650
        imagePanel.zoom(zoom);
2651
      }
2652
      function undo() {
2653
        currentState = undoStack.undo();
2654
        displayState(currentState);
2655
        updateButtonUndoStates();
2656
      }
2657
      function redo() {
2658
        currentState = undoStack.redo();
2659
        displayState(currentState);
2660
        updateButtonUndoStates();
2661
      }
2662
      function save() {
2663
        resolve(currentState.blob);
2664
        win.close();
2665
      }
2666
      function createPanel(items) {
2667
        return global$6.create('Form', {
2668
          layout: 'flex',
2669
          direction: 'row',
2670
          labelGap: 5,
2671
          border: '0 0 1 0',
2672
          align: 'center',
2673
          pack: 'center',
2674
          padding: '0 10 0 10',
2675
          spacing: 5,
2676
          flex: 0,
2677
          minHeight: 60,
2678
          defaults: {
2679
            classes: 'imagetool',
2680
            type: 'button'
2681
          },
2682
          items: items
2683
        });
2684
      }
2685
      var imageResultToBlob = function (ir) {
2686
        return ir.toBlob();
2687
      };
2688
      function createFilterPanel(title, filter) {
2689
        return createPanel(reverseIfRtl([
2690
          {
2691
            text: 'Back',
2692
            onclick: cancel
2693
          },
2694
          {
2695
            type: 'spacer',
2696
            flex: 1
2697
          },
2698
          {
2699
            text: 'Apply',
2700
            subtype: 'primary',
2701
            onclick: applyTempState
2702
          }
2703
        ])).hide().on('show', function () {
2704
          disableUndoRedo();
2705
          ResultConversions.blobToImageResult(currentState.blob).then(function (ir) {
2706
            return filter(ir);
2707
          }).then(imageResultToBlob).then(function (blob) {
2708
            var newTempState = createState(blob);
2709
            displayState(newTempState);
2710
            destroyState(tempState);
2711
            tempState = newTempState;
2712
          });
2713
        });
2714
      }
2715
      function createVariableFilterPanel(title, filter, value, min, max) {
2716
        function update(value) {
2717
          ResultConversions.blobToImageResult(currentState.blob).then(function (ir) {
2718
            return filter(ir, value);
2719
          }).then(imageResultToBlob).then(function (blob) {
2720
            var newTempState = createState(blob);
2721
            displayState(newTempState);
2722
            destroyState(tempState);
2723
            tempState = newTempState;
2724
          });
2725
        }
2726
        return createPanel(reverseIfRtl([
2727
          {
2728
            text: 'Back',
2729
            onclick: cancel
2730
          },
2731
          {
2732
            type: 'spacer',
2733
            flex: 1
2734
          },
2735
          {
2736
            type: 'slider',
2737
            flex: 1,
2738
            ondragend: function (e) {
2739
              update(e.value);
2740
            },
2741
            minValue: editor.rtl ? max : min,
2742
            maxValue: editor.rtl ? min : max,
2743
            value: value,
2744
            previewFilter: floatToPercent
2745
          },
2746
          {
2747
            type: 'spacer',
2748
            flex: 1
2749
          },
2750
          {
2751
            text: 'Apply',
2752
            subtype: 'primary',
2753
            onclick: applyTempState
2754
          }
2755
        ])).hide().on('show', function () {
2756
          this.find('slider').value(value);
2757
          disableUndoRedo();
2758
        });
2759
      }
2760
      function createRgbFilterPanel(title, filter) {
2761
        function update() {
2762
          var r, g, b;
2763
          r = win.find('#r')[0].value();
2764
          g = win.find('#g')[0].value();
2765
          b = win.find('#b')[0].value();
2766
          ResultConversions.blobToImageResult(currentState.blob).then(function (ir) {
2767
            return filter(ir, r, g, b);
2768
          }).then(imageResultToBlob).then(function (blob) {
2769
            var newTempState = createState(blob);
2770
            displayState(newTempState);
2771
            destroyState(tempState);
2772
            tempState = newTempState;
2773
          });
2774
        }
2775
        var min = editor.rtl ? 2 : 0;
2776
        var max = editor.rtl ? 0 : 2;
2777
        return createPanel(reverseIfRtl([
2778
          {
2779
            text: 'Back',
2780
            onclick: cancel
2781
          },
2782
          {
2783
            type: 'spacer',
2784
            flex: 1
2785
          },
2786
          {
2787
            type: 'slider',
2788
            label: 'R',
2789
            name: 'r',
2790
            minValue: min,
2791
            value: 1,
2792
            maxValue: max,
2793
            ondragend: update,
2794
            previewFilter: floatToPercent
2795
          },
2796
          {
2797
            type: 'slider',
2798
            label: 'G',
2799
            name: 'g',
2800
            minValue: min,
2801
            value: 1,
2802
            maxValue: max,
2803
            ondragend: update,
2804
            previewFilter: floatToPercent
2805
          },
2806
          {
2807
            type: 'slider',
2808
            label: 'B',
2809
            name: 'b',
2810
            minValue: min,
2811
            value: 1,
2812
            maxValue: max,
2813
            ondragend: update,
2814
            previewFilter: floatToPercent
2815
          },
2816
          {
2817
            type: 'spacer',
2818
            flex: 1
2819
          },
2820
          {
2821
            text: 'Apply',
2822
            subtype: 'primary',
2823
            onclick: applyTempState
2824
          }
2825
        ])).hide().on('show', function () {
2826
          win.find('#r,#g,#b').value(1);
2827
          disableUndoRedo();
2828
        });
2829
      }
2830
      cropPanel = createPanel(reverseIfRtl([
2831
        {
2832
          text: 'Back',
2833
          onclick: cancel
2834
        },
2835
        {
2836
          type: 'spacer',
2837
          flex: 1
2838
        },
2839
        {
2840
          text: 'Apply',
2841
          subtype: 'primary',
2842
          onclick: crop
2843
        }
2844
      ])).hide().on('show hide', function (e) {
2845
        imagePanel.toggleCropRect(e.type === 'show');
2846
      }).on('show', disableUndoRedo);
2847
      function toggleConstrain(e) {
2848
        if (e.control.value() === true) {
2849
          ratioW = height / width;
2850
          ratioH = width / height;
2851
        }
2852
      }
2853
      resizePanel = createPanel(reverseIfRtl([
2854
        {
2855
          text: 'Back',
2856
          onclick: cancel
2857
        },
2858
        {
2859
          type: 'spacer',
2860
          flex: 1
2861
        },
2862
        {
2863
          type: 'textbox',
2864
          name: 'w',
2865
          label: 'Width',
2866
          size: 4,
2867
          onkeyup: recalcSize
2868
        },
2869
        {
2870
          type: 'textbox',
2871
          name: 'h',
2872
          label: 'Height',
2873
          size: 4,
2874
          onkeyup: recalcSize
2875
        },
2876
        {
2877
          type: 'checkbox',
2878
          name: 'constrain',
2879
          text: 'Constrain proportions',
2880
          checked: true,
2881
          onchange: toggleConstrain
2882
        },
2883
        {
2884
          type: 'spacer',
2885
          flex: 1
2886
        },
2887
        {
2888
          text: 'Apply',
2889
          subtype: 'primary',
2890
          onclick: 'submit'
2891
        }
2892
      ])).hide().on('submit', function (e) {
2893
        var width = parseInt(win.find('#w').value(), 10), height = parseInt(win.find('#h').value(), 10);
2894
        e.preventDefault();
2895
        action(ImageTransformations.resize, width, height)();
2896
        cancel();
2897
      }).on('show', disableUndoRedo);
2898
      flipRotatePanel = createPanel(reverseIfRtl([
2899
        {
2900
          text: 'Back',
2901
          onclick: cancel
2902
        },
2903
        {
2904
          type: 'spacer',
2905
          flex: 1
2906
        },
2907
        {
2908
          icon: 'fliph',
2909
          tooltip: 'Flip horizontally',
2910
          onclick: tempAction(ImageTransformations.flip, 'h')
2911
        },
2912
        {
2913
          icon: 'flipv',
2914
          tooltip: 'Flip vertically',
2915
          onclick: tempAction(ImageTransformations.flip, 'v')
2916
        },
2917
        {
2918
          icon: 'rotateleft',
2919
          tooltip: 'Rotate counterclockwise',
2920
          onclick: tempAction(ImageTransformations.rotate, -90)
2921
        },
2922
        {
2923
          icon: 'rotateright',
2924
          tooltip: 'Rotate clockwise',
2925
          onclick: tempAction(ImageTransformations.rotate, 90)
2926
        },
2927
        {
2928
          type: 'spacer',
2929
          flex: 1
2930
        },
2931
        {
2932
          text: 'Apply',
2933
          subtype: 'primary',
2934
          onclick: applyTempState
2935
        }
2936
      ])).hide().on('show', disableUndoRedo);
2937
      invertPanel = createFilterPanel('Invert', ImageTransformations.invert);
2938
      sharpenPanel = createFilterPanel('Sharpen', ImageTransformations.sharpen);
2939
      embossPanel = createFilterPanel('Emboss', ImageTransformations.emboss);
2940
      brightnessPanel = createVariableFilterPanel('Brightness', ImageTransformations.brightness, 0, -1, 1);
2941
      huePanel = createVariableFilterPanel('Hue', ImageTransformations.hue, 180, 0, 360);
2942
      saturatePanel = createVariableFilterPanel('Saturate', ImageTransformations.saturate, 0, -1, 1);
2943
      contrastPanel = createVariableFilterPanel('Contrast', ImageTransformations.contrast, 0, -1, 1);
2944
      grayscalePanel = createVariableFilterPanel('Grayscale', ImageTransformations.grayscale, 0, 0, 1);
2945
      sepiaPanel = createVariableFilterPanel('Sepia', ImageTransformations.sepia, 0, 0, 1);
2946
      colorizePanel = createRgbFilterPanel('Colorize', ImageTransformations.colorize);
2947
      gammaPanel = createVariableFilterPanel('Gamma', ImageTransformations.gamma, 0, -1, 1);
2948
      exposurePanel = createVariableFilterPanel('Exposure', ImageTransformations.exposure, 1, 0, 2);
2949
      filtersPanel = createPanel(reverseIfRtl([
2950
        {
2951
          text: 'Back',
2952
          onclick: cancel
2953
        },
2954
        {
2955
          type: 'spacer',
2956
          flex: 1
2957
        },
2958
        {
2959
          text: 'hue',
2960
          icon: 'hue',
2961
          onclick: switchPanel(huePanel)
2962
        },
2963
        {
2964
          text: 'saturate',
2965
          icon: 'saturate',
2966
          onclick: switchPanel(saturatePanel)
2967
        },
2968
        {
2969
          text: 'sepia',
2970
          icon: 'sepia',
2971
          onclick: switchPanel(sepiaPanel)
2972
        },
2973
        {
2974
          text: 'emboss',
2975
          icon: 'emboss',
2976
          onclick: switchPanel(embossPanel)
2977
        },
2978
        {
2979
          text: 'exposure',
2980
          icon: 'exposure',
2981
          onclick: switchPanel(exposurePanel)
2982
        },
2983
        {
2984
          type: 'spacer',
2985
          flex: 1
2986
        }
2987
      ])).hide();
2988
      mainPanel = createPanel(reverseIfRtl([
2989
        {
2990
          tooltip: 'Crop',
2991
          icon: 'crop',
2992
          onclick: switchPanel(cropPanel)
2993
        },
2994
        {
2995
          tooltip: 'Resize',
2996
          icon: 'resize2',
2997
          onclick: switchPanel(resizePanel)
2998
        },
2999
        {
3000
          tooltip: 'Orientation',
3001
          icon: 'orientation',
3002
          onclick: switchPanel(flipRotatePanel)
3003
        },
3004
        {
3005
          tooltip: 'Brightness',
3006
          icon: 'sun',
3007
          onclick: switchPanel(brightnessPanel)
3008
        },
3009
        {
3010
          tooltip: 'Sharpen',
3011
          icon: 'sharpen',
3012
          onclick: switchPanel(sharpenPanel)
3013
        },
3014
        {
3015
          tooltip: 'Contrast',
3016
          icon: 'contrast',
3017
          onclick: switchPanel(contrastPanel)
3018
        },
3019
        {
3020
          tooltip: 'Color levels',
3021
          icon: 'drop',
3022
          onclick: switchPanel(colorizePanel)
3023
        },
3024
        {
3025
          tooltip: 'Gamma',
3026
          icon: 'gamma',
3027
          onclick: switchPanel(gammaPanel)
3028
        },
3029
        {
3030
          tooltip: 'Invert',
3031
          icon: 'invert',
3032
          onclick: switchPanel(invertPanel)
3033
        }
3034
      ]));
3035
      imagePanel = ImagePanel.create({
3036
        flex: 1,
3037
        imageSrc: currentState.url
3038
      });
3039
      sidePanel = global$6.create('Container', {
3040
        layout: 'flex',
3041
        direction: 'column',
3042
        pack: 'start',
3043
        border: '0 1 0 0',
3044
        padding: 5,
3045
        spacing: 5,
3046
        items: [
3047
          {
3048
            type: 'button',
3049
            icon: 'undo',
3050
            tooltip: 'Undo',
3051
            name: 'undo',
3052
            onclick: undo
3053
          },
3054
          {
3055
            type: 'button',
3056
            icon: 'redo',
3057
            tooltip: 'Redo',
3058
            name: 'redo',
3059
            onclick: redo
3060
          },
3061
          {
3062
            type: 'button',
3063
            icon: 'zoomin',
3064
            tooltip: 'Zoom in',
3065
            onclick: zoomIn
3066
          },
3067
          {
3068
            type: 'button',
3069
            icon: 'zoomout',
3070
            tooltip: 'Zoom out',
3071
            onclick: zoomOut
3072
          }
3073
        ]
3074
      });
3075
      mainViewContainer = global$6.create('Container', {
3076
        type: 'container',
3077
        layout: 'flex',
3078
        direction: 'row',
3079
        align: 'stretch',
3080
        flex: 1,
3081
        items: reverseIfRtl([
3082
          sidePanel,
3083
          imagePanel
3084
        ])
3085
      });
3086
      panels = [
3087
        mainPanel,
3088
        cropPanel,
3089
        resizePanel,
3090
        flipRotatePanel,
3091
        filtersPanel,
3092
        invertPanel,
3093
        brightnessPanel,
3094
        huePanel,
3095
        saturatePanel,
3096
        contrastPanel,
3097
        grayscalePanel,
3098
        sepiaPanel,
3099
        colorizePanel,
3100
        sharpenPanel,
3101
        embossPanel,
3102
        gammaPanel,
3103
        exposurePanel
3104
      ];
3105
      win = editor.windowManager.open({
3106
        layout: 'flex',
3107
        direction: 'column',
3108
        align: 'stretch',
3109
        minWidth: Math.min(global$5.DOM.getViewPort().w, 800),
3110
        minHeight: Math.min(global$5.DOM.getViewPort().h, 650),
3111
        title: 'Edit image',
3112
        items: panels.concat([mainViewContainer]),
3113
        buttons: reverseIfRtl([
3114
          {
3115
            text: 'Save',
3116
            name: 'save',
3117
            subtype: 'primary',
3118
            onclick: save
3119
          },
3120
          {
3121
            text: 'Cancel',
3122
            onclick: 'close'
3123
          }
3124
        ])
3125
      });
3126
      win.on('close', function () {
3127
        reject();
3128
        destroyStates(undoStack.data);
3129
        undoStack = null;
3130
        tempState = null;
3131
      });
3132
      undoStack.add(currentState);
3133
      updateButtonUndoStates();
3134
      imagePanel.on('load', function () {
3135
        width = imagePanel.imageSize().w;
3136
        height = imagePanel.imageSize().h;
3137
        ratioW = height / width;
3138
        ratioH = width / height;
3139
        win.find('#w').value(width);
3140
        win.find('#h').value(height);
3141
      });
3142
      imagePanel.on('crop', crop);
3143
    }
3144
    function edit(editor, imageResult) {
3145
      return new global$3(function (resolve, reject) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
3146
        return imageResult.toBlob().then(function (blob) {
3147
          open(editor, createState(blob), resolve, reject);
3148
        });
3149
      });
3150
    }
3151
    var Dialog = { edit: edit };
3152
3153
    function getImageSize(img) {
3154
      var width, height;
3155
      function isPxValue(value) {
3156
        return /^[0-9\.]+px$/.test(value);
3157
      }
3158
      width = img.style.width;
3159
      height = img.style.height;
3160
      if (width || height) {
3161
        if (isPxValue(width) && isPxValue(height)) {
3162
          return {
3163
            w: parseInt(width, 10),
3164
            h: parseInt(height, 10)
3165
          };
3166
        }
3167
        return null;
3168
      }
3169
      width = img.width;
3170
      height = img.height;
3171
      if (width && height) {
3172
        return {
3173
          w: parseInt(width, 10),
3174
          h: parseInt(height, 10)
3175
        };
3176
      }
3177
      return null;
3178
    }
3179
    function setImageSize(img, size) {
3180
      var width, height;
3181
      if (size) {
3182
        width = img.style.width;
3183
        height = img.style.height;
3184
        if (width || height) {
3185
          img.style.width = size.w + 'px';
3186
          img.style.height = size.h + 'px';
3187
          img.removeAttribute('data-mce-style');
3188
        }
3189
        width = img.width;
3190
        height = img.height;
3191
        if (width || height) {
3192
          img.setAttribute('width', size.w);
3193
          img.setAttribute('height', size.h);
3194
        }
3195
      }
3196
    }
3197
    function getNaturalImageSize(img) {
3198
      return {
3199
        w: img.naturalWidth,
3200
        h: img.naturalHeight
3201
      };
3202
    }
3203
    var ImageSize$1 = {
3204
      getImageSize: getImageSize,
3205
      setImageSize: setImageSize,
3206
      getNaturalImageSize: getNaturalImageSize
3207
    };
3208
3209
    var typeOf = function (x) {
3210
      if (x === null)
3211
        return 'null';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
3212
      var t = typeof x;
3213
      if (t === 'object' && Array.prototype.isPrototypeOf(x))
3214
        return 'array';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
3215
      if (t === 'object' && String.prototype.isPrototypeOf(x))
3216
        return 'string';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
3217
      return t;
3218
    };
3219
    var isType = function (type) {
3220
      return function (value) {
3221
        return typeOf(value) === type;
3222
      };
3223
    };
3224
    var isFunction = isType('function');
3225
3226
    var find = function (xs, pred) {
3227
      for (var i = 0, len = xs.length; i < len; i++) {
3228
        var x = xs[i];
3229
        if (pred(x, i, xs)) {
3230
          return Option.some(x);
3231
        }
3232
      }
3233
      return Option.none();
3234
    };
3235
    var slice = Array.prototype.slice;
3236
    var from$1 = isFunction(Array.from) ? Array.from : function (x) {
0 ignored issues
show
Unused Code introduced by
The variable from$1 seems to be never used. Consider removing it.
Loading history...
3237
      return slice.call(x);
3238
    };
3239
3240
    function XMLHttpRequest$1 () {
3241
      var f = Global$1.getOrDie('XMLHttpRequest');
3242
      return new f();
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like f should be capitalized.
Loading history...
3243
    }
3244
3245
    var isValue = function (obj) {
3246
      return obj !== null && obj !== undefined;
3247
    };
3248
    var traverse = function (json, path) {
3249
      var value;
3250
      value = path.reduce(function (result, key) {
3251
        return isValue(result) ? result[key] : undefined;
3252
      }, json);
3253
      return isValue(value) ? value : null;
3254
    };
3255
    var requestUrlAsBlob = function (url, headers, withCredentials) {
3256
      return new global$3(function (resolve) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
3257
        var xhr;
3258
        xhr = XMLHttpRequest$1();
3259
        xhr.onreadystatechange = function () {
3260
          if (xhr.readyState === 4) {
3261
            resolve({
3262
              status: xhr.status,
3263
              blob: this.response
3264
            });
3265
          }
3266
        };
3267
        xhr.open('GET', url, true);
3268
        xhr.withCredentials = withCredentials;
3269
        global$1.each(headers, function (value, key) {
3270
          xhr.setRequestHeader(key, value);
3271
        });
3272
        xhr.responseType = 'blob';
3273
        xhr.send();
3274
      });
3275
    };
3276
    var readBlob = function (blob) {
3277
      return new global$3(function (resolve) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
3278
        var fr = FileReader();
3279
        fr.onload = function (e) {
3280
          var data = e.target;
3281
          resolve(data.result);
3282
        };
3283
        fr.readAsText(blob);
3284
      });
3285
    };
3286
    var parseJson = function (text) {
3287
      var json;
3288
      try {
3289
        json = JSON.parse(text);
3290
      } catch (ex) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
3291
      }
3292
      return json;
3293
    };
3294
    var Utils = {
3295
      traverse: traverse,
3296
      readBlob: readBlob,
3297
      requestUrlAsBlob: requestUrlAsBlob,
3298
      parseJson: parseJson
3299
    };
3300
3301
    var friendlyHttpErrors = [
3302
      {
3303
        code: 404,
3304
        message: 'Could not find Image Proxy'
3305
      },
3306
      {
3307
        code: 403,
3308
        message: 'Rejected request'
3309
      },
3310
      {
3311
        code: 0,
3312
        message: 'Incorrect Image Proxy URL'
3313
      }
3314
    ];
3315
    var friendlyServiceErrors = [
3316
      {
3317
        type: 'key_missing',
3318
        message: 'The request did not include an api key.'
3319
      },
3320
      {
3321
        type: 'key_not_found',
3322
        message: 'The provided api key could not be found.'
3323
      },
3324
      {
3325
        type: 'domain_not_trusted',
3326
        message: 'The api key is not valid for the request origins.'
3327
      }
3328
    ];
3329
    var isServiceErrorCode = function (code) {
3330
      return code === 400 || code === 403 || code === 500;
3331
    };
3332
    var getHttpErrorMsg = function (status) {
3333
      var message = find(friendlyHttpErrors, function (error) {
3334
        return status === error.code;
3335
      }).fold(constant('Unknown ImageProxy error'), function (error) {
3336
        return error.message;
3337
      });
3338
      return 'ImageProxy HTTP error: ' + message;
3339
    };
3340
    var handleHttpError = function (status) {
3341
      var message = getHttpErrorMsg(status);
3342
      return global$3.reject(message);
3343
    };
3344
    var getServiceErrorMsg = function (type) {
3345
      return find(friendlyServiceErrors, function (error) {
3346
        return error.type === type;
3347
      }).fold(constant('Unknown service error'), function (error) {
3348
        return error.message;
3349
      });
3350
    };
3351
    var getServiceError = function (text) {
3352
      var serviceError = Utils.parseJson(text);
3353
      var errorType = Utils.traverse(serviceError, [
3354
        'error',
3355
        'type'
3356
      ]);
3357
      var errorMsg = errorType ? getServiceErrorMsg(errorType) : 'Invalid JSON in service error message';
3358
      return 'ImageProxy Service error: ' + errorMsg;
3359
    };
3360
    var handleServiceError = function (status, blob) {
3361
      return Utils.readBlob(blob).then(function (text) {
3362
        var serviceError = getServiceError(text);
3363
        return global$3.reject(serviceError);
3364
      });
3365
    };
3366
    var handleServiceErrorResponse = function (status, blob) {
3367
      return isServiceErrorCode(status) ? handleServiceError(status, blob) : handleHttpError(status);
3368
    };
3369
    var Errors = {
3370
      handleServiceErrorResponse: handleServiceErrorResponse,
3371
      handleHttpError: handleHttpError,
3372
      getHttpErrorMsg: getHttpErrorMsg,
3373
      getServiceErrorMsg: getServiceErrorMsg
3374
    };
3375
3376
    var appendApiKey = function (url, apiKey) {
3377
      var separator = url.indexOf('?') === -1 ? '?' : '&';
3378
      if (/[?&]apiKey=/.test(url) || !apiKey) {
3379
        return url;
3380
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
3381
        return url + separator + 'apiKey=' + encodeURIComponent(apiKey);
3382
      }
3383
    };
3384
    var requestServiceBlob = function (url, apiKey) {
3385
      var headers = {
3386
        'Content-Type': 'application/json;charset=UTF-8',
3387
        'tiny-api-key': apiKey
3388
      };
3389
      return Utils.requestUrlAsBlob(appendApiKey(url, apiKey), headers, false).then(function (result) {
3390
        return result.status < 200 || result.status >= 300 ? Errors.handleServiceErrorResponse(result.status, result.blob) : global$3.resolve(result.blob);
3391
      });
3392
    };
3393
    function requestBlob(url, withCredentials) {
3394
      return Utils.requestUrlAsBlob(url, {}, withCredentials).then(function (result) {
3395
        return result.status < 200 || result.status >= 300 ? Errors.handleHttpError(result.status) : global$3.resolve(result.blob);
3396
      });
3397
    }
3398
    var getUrl = function (url, apiKey, withCredentials) {
3399
      return apiKey ? requestServiceBlob(url, apiKey) : requestBlob(url, withCredentials);
3400
    };
3401
    var Proxy = { getUrl: getUrl };
0 ignored issues
show
Comprehensibility introduced by
You are shadowing the built-in type Proxy. This makes code hard to read, consider using a different name.
Loading history...
3402
3403
    var count$1 = 0;
3404
    var isEditableImage = function (editor, img) {
3405
      var selectorMatched = editor.dom.is(img, 'img:not([data-mce-object],[data-mce-placeholder])');
3406
      return selectorMatched && (isLocalImage(editor, img) || isCorsImage(editor, img) || editor.settings.imagetools_proxy);
3407
    };
3408
    var displayError = function (editor, error) {
3409
      editor.notificationManager.open({
3410
        text: error,
3411
        type: 'error'
3412
      });
3413
    };
3414
    var getSelectedImage = function (editor) {
3415
      return editor.selection.getNode();
3416
    };
3417
    var extractFilename = function (editor, url) {
3418
      var m = url.match(/\/([^\/\?]+)?\.(?:jpeg|jpg|png|gif)(?:\?|$)/i);
3419
      if (m) {
3420
        return editor.dom.encode(m[1]);
3421
      }
3422
      return null;
3423
    };
3424
    var createId = function () {
3425
      return 'imagetools' + count$1++;
3426
    };
3427
    var isLocalImage = function (editor, img) {
3428
      var url = img.src;
3429
      return url.indexOf('data:') === 0 || url.indexOf('blob:') === 0 || new global$4(url).host === editor.documentBaseURI.host;
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$4 should be capitalized.
Loading history...
3430
    };
3431
    var isCorsImage = function (editor, img) {
3432
      return global$1.inArray(getCorsHosts(editor), new global$4(img.src).host) !== -1;
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$4 should be capitalized.
Loading history...
3433
    };
3434
    var isCorsWithCredentialsImage = function (editor, img) {
3435
      return global$1.inArray(getCredentialsHosts(editor), new global$4(img.src).host) !== -1;
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$4 should be capitalized.
Loading history...
3436
    };
3437
    var imageToBlob$2 = function (editor, img) {
3438
      var src = img.src, apiKey;
3439
      if (isCorsImage(editor, img)) {
3440
        return Proxy.getUrl(img.src, null, isCorsWithCredentialsImage(editor, img));
3441
      }
3442
      if (!isLocalImage(editor, img)) {
3443
        src = getProxyUrl(editor);
3444
        src += (src.indexOf('?') === -1 ? '?' : '&') + 'url=' + encodeURIComponent(img.src);
3445
        apiKey = getApiKey(editor);
3446
        return Proxy.getUrl(src, apiKey, false);
3447
      }
3448
      return BlobConversions.imageToBlob(img);
3449
    };
3450
    var findSelectedBlob = function (editor) {
3451
      var blobInfo;
3452
      blobInfo = editor.editorUpload.blobCache.getByUri(getSelectedImage(editor).src);
3453
      if (blobInfo) {
3454
        return global$3.resolve(blobInfo.blob());
3455
      }
3456
      return imageToBlob$2(editor, getSelectedImage(editor));
3457
    };
3458
    var startTimedUpload = function (editor, imageUploadTimerState) {
3459
      var imageUploadTimer = global$2.setEditorTimeout(editor, function () {
3460
        editor.editorUpload.uploadImagesAuto();
3461
      }, getUploadTimeout(editor));
3462
      imageUploadTimerState.set(imageUploadTimer);
3463
    };
3464
    var cancelTimedUpload = function (imageUploadTimerState) {
3465
      clearTimeout(imageUploadTimerState.get());
3466
    };
3467
    var updateSelectedImage = function (editor, ir, uploadImmediately, imageUploadTimerState, size) {
3468
      return ir.toBlob().then(function (blob) {
3469
        var uri, name, blobCache, blobInfo, selectedImage;
3470
        blobCache = editor.editorUpload.blobCache;
3471
        selectedImage = getSelectedImage(editor);
3472
        uri = selectedImage.src;
3473
        if (shouldReuseFilename(editor)) {
3474
          blobInfo = blobCache.getByUri(uri);
3475
          if (blobInfo) {
3476
            uri = blobInfo.uri();
3477
            name = blobInfo.name();
3478
          } else {
3479
            name = extractFilename(editor, uri);
3480
          }
3481
        }
3482
        blobInfo = blobCache.create({
3483
          id: createId(),
3484
          blob: blob,
3485
          base64: ir.toBase64(),
3486
          uri: uri,
3487
          name: name
0 ignored issues
show
Bug introduced by
The variable name does not seem to be initialized in case shouldReuseFilename(editor) on line 3473 is false. Are you sure this can never be the case?
Loading history...
3488
        });
3489
        blobCache.add(blobInfo);
3490
        editor.undoManager.transact(function () {
3491
          function imageLoadedHandler() {
3492
            editor.$(selectedImage).off('load', imageLoadedHandler);
3493
            editor.nodeChanged();
3494
            if (uploadImmediately) {
3495
              editor.editorUpload.uploadImagesAuto();
3496
            } else {
3497
              cancelTimedUpload(imageUploadTimerState);
3498
              startTimedUpload(editor, imageUploadTimerState);
3499
            }
3500
          }
3501
          editor.$(selectedImage).on('load', imageLoadedHandler);
3502
          if (size) {
3503
            editor.$(selectedImage).attr({
3504
              width: size.w,
3505
              height: size.h
3506
            });
3507
          }
3508
          editor.$(selectedImage).attr({ src: blobInfo.blobUri() }).removeAttr('data-mce-src');
3509
        });
3510
        return blobInfo;
3511
      });
3512
    };
3513
    var selectedImageOperation = function (editor, imageUploadTimerState, fn, size) {
3514
      return function () {
3515
        return editor._scanForImages().then(curry(findSelectedBlob, editor)).then(ResultConversions.blobToImageResult).then(fn).then(function (imageResult) {
3516
          return updateSelectedImage(editor, imageResult, false, imageUploadTimerState, size);
3517
        }, function (error) {
3518
          displayError(editor, error);
3519
        });
3520
      };
3521
    };
3522
    var rotate$2 = function (editor, imageUploadTimerState, angle) {
3523
      return function () {
3524
        var size = ImageSize$1.getImageSize(getSelectedImage(editor));
3525
        var flippedSize = size ? {
3526
          w: size.h,
3527
          h: size.w
3528
        } : null;
3529
        return selectedImageOperation(editor, imageUploadTimerState, function (imageResult) {
3530
          return ImageTransformations.rotate(imageResult, angle);
3531
        }, flippedSize)();
3532
      };
3533
    };
3534
    var flip$2 = function (editor, imageUploadTimerState, axis) {
3535
      return function () {
3536
        return selectedImageOperation(editor, imageUploadTimerState, function (imageResult) {
3537
          return ImageTransformations.flip(imageResult, axis);
3538
        })();
3539
      };
3540
    };
3541
    var editImageDialog = function (editor, imageUploadTimerState) {
3542
      return function () {
3543
        var img = getSelectedImage(editor), originalSize = ImageSize$1.getNaturalImageSize(img);
3544
        var handleDialogBlob = function (blob) {
3545
          return new global$3(function (resolve) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like global$3 should be capitalized.
Loading history...
3546
            BlobConversions.blobToImage(blob).then(function (newImage) {
3547
              var newSize = ImageSize$1.getNaturalImageSize(newImage);
3548
              if (originalSize.w !== newSize.w || originalSize.h !== newSize.h) {
3549
                if (ImageSize$1.getImageSize(img)) {
3550
                  ImageSize$1.setImageSize(img, newSize);
3551
                }
3552
              }
3553
              URL$1.revokeObjectURL(newImage.src);
3554
              resolve(blob);
3555
            });
3556
          });
3557
        };
3558
        var openDialog = function (editor, imageResult) {
3559
          return Dialog.edit(editor, imageResult).then(handleDialogBlob).then(ResultConversions.blobToImageResult).then(function (imageResult) {
3560
            return updateSelectedImage(editor, imageResult, true, imageUploadTimerState);
3561
          }, function () {
3562
          });
3563
        };
3564
        findSelectedBlob(editor).then(ResultConversions.blobToImageResult).then(curry(openDialog, editor), function (error) {
3565
          displayError(editor, error);
3566
        });
3567
      };
3568
    };
3569
    var Actions = {
3570
      rotate: rotate$2,
3571
      flip: flip$2,
3572
      editImageDialog: editImageDialog,
3573
      isEditableImage: isEditableImage,
3574
      cancelTimedUpload: cancelTimedUpload
3575
    };
3576
3577
    var register = function (editor, imageUploadTimerState) {
3578
      global$1.each({
3579
        mceImageRotateLeft: Actions.rotate(editor, imageUploadTimerState, -90),
3580
        mceImageRotateRight: Actions.rotate(editor, imageUploadTimerState, 90),
3581
        mceImageFlipVertical: Actions.flip(editor, imageUploadTimerState, 'v'),
3582
        mceImageFlipHorizontal: Actions.flip(editor, imageUploadTimerState, 'h'),
3583
        mceEditImage: Actions.editImageDialog(editor, imageUploadTimerState)
3584
      }, function (fn, cmd) {
3585
        editor.addCommand(cmd, fn);
3586
      });
3587
    };
3588
    var Commands = { register: register };
3589
3590
    var setup = function (editor, imageUploadTimerState, lastSelectedImageState) {
3591
      editor.on('NodeChange', function (e) {
3592
        var lastSelectedImage = lastSelectedImageState.get();
3593
        if (lastSelectedImage && lastSelectedImage.src !== e.element.src) {
3594
          Actions.cancelTimedUpload(imageUploadTimerState);
3595
          editor.editorUpload.uploadImagesAuto();
3596
          lastSelectedImageState.set(null);
3597
        }
3598
        if (Actions.isEditableImage(editor, e.element)) {
3599
          lastSelectedImageState.set(e.element);
3600
        }
3601
      });
3602
    };
3603
    var UploadSelectedImage = { setup: setup };
3604
3605
    var register$1 = function (editor) {
3606
      editor.addButton('rotateleft', {
3607
        title: 'Rotate counterclockwise',
3608
        cmd: 'mceImageRotateLeft'
3609
      });
3610
      editor.addButton('rotateright', {
3611
        title: 'Rotate clockwise',
3612
        cmd: 'mceImageRotateRight'
3613
      });
3614
      editor.addButton('flipv', {
3615
        title: 'Flip vertically',
3616
        cmd: 'mceImageFlipVertical'
3617
      });
3618
      editor.addButton('fliph', {
3619
        title: 'Flip horizontally',
3620
        cmd: 'mceImageFlipHorizontal'
3621
      });
3622
      editor.addButton('editimage', {
3623
        title: 'Edit image',
3624
        cmd: 'mceEditImage'
3625
      });
3626
      editor.addButton('imageoptions', {
3627
        title: 'Image options',
3628
        icon: 'options',
3629
        cmd: 'mceImage'
3630
      });
3631
    };
3632
    var Buttons = { register: register$1 };
3633
3634
    var register$2 = function (editor) {
3635
      editor.addContextToolbar(curry(Actions.isEditableImage, editor), getToolbarItems(editor));
3636
    };
3637
    var ContextToolbar = { register: register$2 };
3638
3639
    global.add('imagetools', function (editor) {
3640
      var imageUploadTimerState = Cell(0);
3641
      var lastSelectedImageState = Cell(null);
3642
      Commands.register(editor, imageUploadTimerState);
3643
      Buttons.register(editor);
3644
      ContextToolbar.register(editor);
3645
      UploadSelectedImage.setup(editor, imageUploadTimerState, lastSelectedImageState);
3646
    });
3647
    function Plugin () {
3648
    }
3649
3650
    return Plugin;
3651
3652
}());
3653
})();
3654