PageRenderTime 81ms CodeModel.GetById 46ms RepoModel.GetById 0ms app.codeStats 0ms

/thirdparty/tinymce/plugins/imagetools/plugin.js

http://github.com/silverstripe/sapphire
JavaScript | 2731 lines | 2031 code | 452 blank | 248 comment | 181 complexity | b9a5328b7232a4e968a0d742c2e976bc MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, CC-BY-3.0, GPL-2.0, AGPL-1.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. (function () {
  2. var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)}
  3. // Used when there is no 'main' module.
  4. // The name is probably (hopefully) unique so minification removes for releases.
  5. var register_3795 = function (id) {
  6. var module = dem(id);
  7. var fragments = id.split('.');
  8. var target = Function('return this;')();
  9. for (var i = 0; i < fragments.length - 1; ++i) {
  10. if (target[fragments[i]] === undefined)
  11. target[fragments[i]] = {};
  12. target = target[fragments[i]];
  13. }
  14. target[fragments[fragments.length - 1]] = module;
  15. };
  16. var instantiate = function (id) {
  17. var actual = defs[id];
  18. var dependencies = actual.deps;
  19. var definition = actual.defn;
  20. var len = dependencies.length;
  21. var instances = new Array(len);
  22. for (var i = 0; i < len; ++i)
  23. instances[i] = dem(dependencies[i]);
  24. var defResult = definition.apply(null, instances);
  25. if (defResult === undefined)
  26. throw 'module [' + id + '] returned undefined';
  27. actual.instance = defResult;
  28. };
  29. var def = function (id, dependencies, definition) {
  30. if (typeof id !== 'string')
  31. throw 'module id must be a string';
  32. else if (dependencies === undefined)
  33. throw 'no dependencies for ' + id;
  34. else if (definition === undefined)
  35. throw 'no definition function for ' + id;
  36. defs[id] = {
  37. deps: dependencies,
  38. defn: definition,
  39. instance: undefined
  40. };
  41. };
  42. var dem = function (id) {
  43. var actual = defs[id];
  44. if (actual === undefined)
  45. throw 'module [' + id + '] was undefined';
  46. else if (actual.instance === undefined)
  47. instantiate(id);
  48. return actual.instance;
  49. };
  50. var req = function (ids, callback) {
  51. var len = ids.length;
  52. var instances = new Array(len);
  53. for (var i = 0; i < len; ++i)
  54. instances.push(dem(ids[i]));
  55. callback.apply(null, callback);
  56. };
  57. var ephox = {};
  58. ephox.bolt = {
  59. module: {
  60. api: {
  61. define: def,
  62. require: req,
  63. demand: dem
  64. }
  65. }
  66. };
  67. var define = def;
  68. var require = req;
  69. var demand = dem;
  70. // this helps with minificiation when using a lot of global references
  71. var defineGlobal = function (id, ref) {
  72. define(id, [], function () { return ref; });
  73. };
  74. /*jsc
  75. ["tinymce/imagetoolsplugin/Plugin","global!tinymce.PluginManager","global!tinymce.Env","global!tinymce.util.Promise","global!tinymce.util.URI","global!tinymce.util.Tools","global!tinymce.util.Delay","ephox/imagetools/api/ImageTransformations","ephox/imagetools/api/BlobConversions","tinymce/imagetoolsplugin/Dialog","ephox/imagetools/transformations/Filters","ephox/imagetools/transformations/ImageTools","ephox/imagetools/util/Conversions","global!tinymce.dom.DOMUtils","global!tinymce.ui.Factory","global!tinymce.ui.Form","global!tinymce.ui.Container","tinymce/imagetoolsplugin/ImagePanel","tinymce/imagetoolsplugin/UndoStack","ephox/imagetools/util/Canvas","ephox/imagetools/util/ImageSize","ephox/imagetools/util/Promise","ephox/imagetools/util/Mime","ephox/imagetools/transformations/ColorMatrix","global!tinymce.ui.Control","global!tinymce.ui.DragHelper","global!tinymce.geom.Rect","tinymce/imagetoolsplugin/CropRect","global!tinymce.dom.DomQuery","global!tinymce.util.Observable","global!tinymce.util.VK"]
  76. jsc*/
  77. defineGlobal("global!tinymce.PluginManager", tinymce.PluginManager);
  78. defineGlobal("global!tinymce.Env", tinymce.Env);
  79. defineGlobal("global!tinymce.util.Promise", tinymce.util.Promise);
  80. defineGlobal("global!tinymce.util.URI", tinymce.util.URI);
  81. defineGlobal("global!tinymce.util.Tools", tinymce.util.Tools);
  82. defineGlobal("global!tinymce.util.Delay", tinymce.util.Delay);
  83. /**
  84. * Canvas.js
  85. *
  86. * Released under LGPL License.
  87. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  88. *
  89. * License: http://www.tinymce.com/license
  90. * Contributing: http://www.tinymce.com/contributing
  91. */
  92. /**
  93. * Contains various canvas functions.
  94. */
  95. define("ephox/imagetools/util/Canvas", [], function() {
  96. function create(width, height) {
  97. return resize(document.createElement('canvas'), width, height);
  98. }
  99. function get2dContext(canvas) {
  100. return canvas.getContext("2d");
  101. }
  102. function resize(canvas, width, height) {
  103. canvas.width = width;
  104. canvas.height = height;
  105. return canvas;
  106. }
  107. return {
  108. create: create,
  109. resize: resize,
  110. get2dContext: get2dContext
  111. };
  112. });
  113. /**
  114. * ImageSize.js
  115. *
  116. * Released under LGPL License.
  117. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  118. *
  119. * License: http://www.tinymce.com/license
  120. * Contributing: http://www.tinymce.com/contributing
  121. */
  122. /**
  123. * Returns the size of images.
  124. */
  125. define("ephox/imagetools/util/ImageSize", [], function() {
  126. function getWidth(image) {
  127. return image.naturalWidth || image.width;
  128. }
  129. function getHeight(image) {
  130. return image.naturalHeight || image.height;
  131. }
  132. return {
  133. getWidth: getWidth,
  134. getHeight: getHeight
  135. };
  136. });
  137. /**
  138. * Promise.js
  139. *
  140. * Released under LGPL License.
  141. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  142. *
  143. * Promise polyfill under MIT license: https://github.com/taylorhakes/promise-polyfill
  144. *
  145. * License: http://www.tinymce.com/license
  146. * Contributing: http://www.tinymce.com/contributing
  147. */
  148. /* eslint-disable */
  149. /* jshint ignore:start */
  150. /**
  151. * Modifed to be a feature fill and wrapped as tinymce module.
  152. */
  153. define("ephox/imagetools/util/Promise", [], function() {
  154. if (window.Promise) {
  155. return window.Promise;
  156. }
  157. // Use polyfill for setImmediate for performance gains
  158. var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) ||
  159. function(fn) { setTimeout(fn, 1); };
  160. // Polyfill for Function.prototype.bind
  161. function bind(fn, thisArg) {
  162. return function() {
  163. fn.apply(thisArg, arguments);
  164. };
  165. }
  166. var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]"; };
  167. function Promise(fn) {
  168. if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
  169. if (typeof fn !== 'function') throw new TypeError('not a function');
  170. this._state = null;
  171. this._value = null;
  172. this._deferreds = [];
  173. doResolve(fn, bind(resolve, this), bind(reject, this));
  174. }
  175. function handle(deferred) {
  176. var me = this;
  177. if (this._state === null) {
  178. this._deferreds.push(deferred);
  179. return;
  180. }
  181. asap(function() {
  182. var cb = me._state ? deferred.onFulfilled : deferred.onRejected;
  183. if (cb === null) {
  184. (me._state ? deferred.resolve : deferred.reject)(me._value);
  185. return;
  186. }
  187. var ret;
  188. try {
  189. ret = cb(me._value);
  190. }
  191. catch (e) {
  192. deferred.reject(e);
  193. return;
  194. }
  195. deferred.resolve(ret);
  196. });
  197. }
  198. function resolve(newValue) {
  199. try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  200. if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.');
  201. if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
  202. var then = newValue.then;
  203. if (typeof then === 'function') {
  204. doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this));
  205. return;
  206. }
  207. }
  208. this._state = true;
  209. this._value = newValue;
  210. finale.call(this);
  211. } catch (e) { reject.call(this, e); }
  212. }
  213. function reject(newValue) {
  214. this._state = false;
  215. this._value = newValue;
  216. finale.call(this);
  217. }
  218. function finale() {
  219. for (var i = 0, len = this._deferreds.length; i < len; i++) {
  220. handle.call(this, this._deferreds[i]);
  221. }
  222. this._deferreds = null;
  223. }
  224. function Handler(onFulfilled, onRejected, resolve, reject){
  225. this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  226. this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  227. this.resolve = resolve;
  228. this.reject = reject;
  229. }
  230. /**
  231. * Take a potentially misbehaving resolver function and make sure
  232. * onFulfilled and onRejected are only called once.
  233. *
  234. * Makes no guarantees about asynchrony.
  235. */
  236. function doResolve(fn, onFulfilled, onRejected) {
  237. var done = false;
  238. try {
  239. fn(function (value) {
  240. if (done) return;
  241. done = true;
  242. onFulfilled(value);
  243. }, function (reason) {
  244. if (done) return;
  245. done = true;
  246. onRejected(reason);
  247. });
  248. } catch (ex) {
  249. if (done) return;
  250. done = true;
  251. onRejected(ex);
  252. }
  253. }
  254. Promise.prototype['catch'] = function (onRejected) {
  255. return this.then(null, onRejected);
  256. };
  257. Promise.prototype.then = function(onFulfilled, onRejected) {
  258. var me = this;
  259. return new Promise(function(resolve, reject) {
  260. handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject));
  261. });
  262. };
  263. Promise.all = function () {
  264. var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments);
  265. return new Promise(function (resolve, reject) {
  266. if (args.length === 0) return resolve([]);
  267. var remaining = args.length;
  268. function res(i, val) {
  269. try {
  270. if (val && (typeof val === 'object' || typeof val === 'function')) {
  271. var then = val.then;
  272. if (typeof then === 'function') {
  273. then.call(val, function (val) { res(i, val); }, reject);
  274. return;
  275. }
  276. }
  277. args[i] = val;
  278. if (--remaining === 0) {
  279. resolve(args);
  280. }
  281. } catch (ex) {
  282. reject(ex);
  283. }
  284. }
  285. for (var i = 0; i < args.length; i++) {
  286. res(i, args[i]);
  287. }
  288. });
  289. };
  290. Promise.resolve = function (value) {
  291. if (value && typeof value === 'object' && value.constructor === Promise) {
  292. return value;
  293. }
  294. return new Promise(function (resolve) {
  295. resolve(value);
  296. });
  297. };
  298. Promise.reject = function (value) {
  299. return new Promise(function (resolve, reject) {
  300. reject(value);
  301. });
  302. };
  303. Promise.race = function (values) {
  304. return new Promise(function (resolve, reject) {
  305. for(var i = 0, len = values.length; i < len; i++) {
  306. values[i].then(resolve, reject);
  307. }
  308. });
  309. };
  310. return Promise;
  311. });
  312. /* jshint ignore:end */
  313. /* eslint-enable */
  314. /**
  315. * Mime.js
  316. *
  317. * Released under LGPL License.
  318. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  319. *
  320. * License: http://www.tinymce.com/license
  321. * Contributing: http://www.tinymce.com/contributing
  322. */
  323. /**
  324. * Returns mime types for uris.
  325. */
  326. define("ephox/imagetools/util/Mime", [], function() {
  327. function getUriPathName(uri) {
  328. var a = document.createElement('a');
  329. a.href = uri;
  330. return a.pathname;
  331. }
  332. function guessMimeType(uri) {
  333. var parts = getUriPathName(uri).split('.'),
  334. ext = parts[parts.length - 1],
  335. mimes = {
  336. 'jpg': 'image/jpeg',
  337. 'jpeg': 'image/jpeg',
  338. 'png': 'image/png'
  339. };
  340. if (ext) {
  341. ext = ext.toLowerCase();
  342. }
  343. return mimes[ext];
  344. }
  345. return {
  346. guessMimeType: guessMimeType
  347. };
  348. });
  349. /**
  350. * Conversions.js
  351. *
  352. * Released under LGPL License.
  353. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  354. *
  355. * License: http://www.tinymce.com/license
  356. * Contributing: http://www.tinymce.com/contributing
  357. */
  358. /**
  359. * Converts blob/uris/images back and forth.
  360. */
  361. define("ephox/imagetools/util/Conversions", [
  362. "ephox/imagetools/util/Promise",
  363. "ephox/imagetools/util/Canvas",
  364. "ephox/imagetools/util/Mime",
  365. "ephox/imagetools/util/ImageSize"
  366. ], function(Promise, Canvas, Mime, ImageSize) {
  367. function loadImage(image) {
  368. return new Promise(function(resolve) {
  369. function loaded() {
  370. image.removeEventListener('load', loaded);
  371. resolve(image);
  372. }
  373. if (image.complete) {
  374. resolve(image);
  375. } else {
  376. image.addEventListener('load', loaded);
  377. }
  378. });
  379. }
  380. function imageToCanvas(image) {
  381. return loadImage(image).then(function(image) {
  382. var context, canvas;
  383. canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image));
  384. context = Canvas.get2dContext(canvas);
  385. context.drawImage(image, 0, 0);
  386. return canvas;
  387. });
  388. }
  389. function imageToBlob(image) {
  390. return loadImage(image).then(function(image) {
  391. var src = image.src;
  392. if (src.indexOf('blob:') === 0) {
  393. return blobUriToBlob(src);
  394. }
  395. if (src.indexOf('data:') === 0) {
  396. return dataUriToBlob(src);
  397. }
  398. return imageToCanvas(image).then(function(canvas) {
  399. return dataUriToBlob(canvas.toDataURL(Mime.guessMimeType(src)));
  400. });
  401. });
  402. }
  403. function blobToImage(blob) {
  404. return new Promise(function(resolve) {
  405. var image = new Image();
  406. function loaded() {
  407. image.removeEventListener('load', loaded);
  408. resolve(image);
  409. }
  410. image.addEventListener('load', loaded);
  411. image.src = URL.createObjectURL(blob);
  412. if (image.complete) {
  413. loaded();
  414. }
  415. });
  416. }
  417. function blobUriToBlob(url) {
  418. return new Promise(function(resolve) {
  419. var xhr = new XMLHttpRequest();
  420. xhr.open('GET', url, true);
  421. xhr.responseType = 'blob';
  422. xhr.onload = function() {
  423. if (this.status == 200) {
  424. resolve(this.response);
  425. }
  426. };
  427. xhr.send();
  428. });
  429. }
  430. function dataUriToBlob(uri) {
  431. return new Promise(function(resolve) {
  432. var str, arr, i, matches, type, blobBuilder;
  433. uri = uri.split(',');
  434. matches = /data:([^;]+)/.exec(uri[0]);
  435. if (matches) {
  436. type = matches[1];
  437. }
  438. str = atob(uri[1]);
  439. if (window.WebKitBlobBuilder) {
  440. /*globals WebKitBlobBuilder:false */
  441. blobBuilder = new WebKitBlobBuilder();
  442. arr = new ArrayBuffer(str.length);
  443. for (i = 0; i < arr.length; i++) {
  444. arr[i] = str.charCodeAt(i);
  445. }
  446. blobBuilder.append(arr);
  447. resolve(blobBuilder.getBlob(type));
  448. return;
  449. }
  450. arr = new Uint8Array(str.length);
  451. for (i = 0; i < arr.length; i++) {
  452. arr[i] = str.charCodeAt(i);
  453. }
  454. resolve(new Blob([arr], {type: type}));
  455. });
  456. }
  457. function uriToBlob(url) {
  458. if (url.indexOf('blob:') === 0) {
  459. return blobUriToBlob(url);
  460. }
  461. if (url.indexOf('data:') === 0) {
  462. return dataUriToBlob(url);
  463. }
  464. return null;
  465. }
  466. function canvasToBlob(canvas, type) {
  467. return dataUriToBlob(canvas.toDataURL(type));
  468. }
  469. function blobToDataUri(blob) {
  470. return new Promise(function(resolve) {
  471. var reader = new FileReader();
  472. reader.onloadend = function() {
  473. resolve(reader.result);
  474. };
  475. reader.readAsDataURL(blob);
  476. });
  477. }
  478. function blobToBase64(blob) {
  479. return blobToDataUri(blob).then(function(dataUri) {
  480. return dataUri.split(',')[1];
  481. });
  482. }
  483. function revokeImageUrl(image) {
  484. URL.revokeObjectURL(image.src);
  485. }
  486. return {
  487. // used outside
  488. blobToImage: blobToImage,
  489. // used outside
  490. imageToBlob: imageToBlob,
  491. // used outside
  492. blobToDataUri: blobToDataUri,
  493. // used outside
  494. blobToBase64: blobToBase64,
  495. // helper method
  496. imageToCanvas: imageToCanvas,
  497. // helper method
  498. canvasToBlob: canvasToBlob,
  499. // helper method
  500. revokeImageUrl: revokeImageUrl,
  501. // helper method
  502. uriToBlob: uriToBlob
  503. };
  504. });
  505. /**
  506. * ImageTools.js
  507. *
  508. * Released under LGPL License.
  509. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  510. *
  511. * License: http://www.tinymce.com/license
  512. * Contributing: http://www.tinymce.com/contributing
  513. *
  514. * Some of the matrix calculations and constants are from the EaselJS library released under MIT:
  515. * https://github.com/CreateJS/EaselJS/blob/master/src/easeljs/filters/ColorMatrix.js
  516. */
  517. /**
  518. * Various operations for color matrices.
  519. */
  520. define("ephox/imagetools/transformations/ColorMatrix", [], function() {
  521. function clamp(value, min, max) {
  522. value = parseFloat(value);
  523. if (value > max) {
  524. value = max;
  525. } else if (value < min) {
  526. value = min;
  527. }
  528. return value;
  529. }
  530. function identity() {
  531. return [
  532. 1, 0, 0, 0, 0,
  533. 0, 1, 0, 0, 0,
  534. 0, 0, 1, 0, 0,
  535. 0, 0, 0, 1, 0,
  536. 0, 0, 0, 0, 1
  537. ];
  538. }
  539. var DELTA_INDEX = [
  540. 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
  541. 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
  542. 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
  543. 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
  544. 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
  545. 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
  546. 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
  547. 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
  548. 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
  549. 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
  550. 10.0
  551. ];
  552. function multiply(matrix1, matrix2) {
  553. var i, j, k, val, col = [], out = new Array(10);
  554. for (i = 0; i < 5; i++) {
  555. for (j = 0; j < 5; j++) {
  556. col[j] = matrix2[j + i * 5];
  557. }
  558. for (j = 0; j < 5; j++) {
  559. val = 0;
  560. for (k = 0; k < 5; k++) {
  561. val += matrix1[j + k * 5] * col[k];
  562. }
  563. out[j + i * 5] = val;
  564. }
  565. }
  566. return out;
  567. }
  568. function adjust(matrix, adjustValue) {
  569. adjustValue = clamp(adjustValue, 0, 1);
  570. return matrix.map(function(value, index) {
  571. if (index % 6 === 0) {
  572. value = 1.0 - ((1 - value) * adjustValue);
  573. } else {
  574. value *= adjustValue;
  575. }
  576. return clamp(value, 0, 1);
  577. });
  578. }
  579. function adjustContrast(matrix, value) {
  580. var x;
  581. value = clamp(value, -1, 1);
  582. value *= 100;
  583. if (value < 0) {
  584. x = 127 + value / 100 * 127;
  585. } else {
  586. x = value % 1;
  587. if (x === 0) {
  588. x = DELTA_INDEX[value];
  589. } else {
  590. // use linear interpolation for more granularity.
  591. x = DELTA_INDEX[(Math.floor(value))] * (1 - x) + DELTA_INDEX[(Math.floor(value)) + 1] * x;
  592. }
  593. x = x * 127 + 127;
  594. }
  595. return multiply(matrix, [
  596. x / 127, 0, 0, 0, 0.5 * (127 - x),
  597. 0, x / 127, 0, 0, 0.5 * (127 - x),
  598. 0, 0, x / 127, 0, 0.5 * (127 - x),
  599. 0, 0, 0, 1, 0,
  600. 0, 0, 0, 0, 1
  601. ]);
  602. }
  603. function adjustSaturation(matrix, value) {
  604. var x, lumR, lumG, lumB;
  605. value = clamp(value, -1, 1);
  606. x = 1 + ((value > 0) ? 3 * value : value);
  607. lumR = 0.3086;
  608. lumG = 0.6094;
  609. lumB = 0.0820;
  610. return multiply(matrix, [
  611. lumR * (1 - x) + x, lumG * (1 - x), lumB * (1 - x), 0, 0,
  612. lumR * (1 - x), lumG * (1 - x) + x, lumB * (1 - x), 0, 0,
  613. lumR * (1 - x), lumG * (1 - x), lumB * (1 - x) + x, 0, 0,
  614. 0, 0, 0, 1, 0,
  615. 0, 0, 0, 0, 1
  616. ]);
  617. }
  618. function adjustHue(matrix, angle) {
  619. var cosVal, sinVal, lumR, lumG, lumB;
  620. angle = clamp(angle, -180, 180) / 180 * Math.PI;
  621. cosVal = Math.cos(angle);
  622. sinVal = Math.sin(angle);
  623. lumR = 0.213;
  624. lumG = 0.715;
  625. lumB = 0.072;
  626. return multiply(matrix, [
  627. lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG),
  628. lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
  629. lumR + cosVal * (-lumR) + sinVal * (0.143), lumG + cosVal * (1 - lumG) + sinVal * (0.140),
  630. lumB + cosVal * (-lumB) + sinVal * (-0.283), 0, 0,
  631. lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG),
  632. lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0,
  633. 0, 0, 0, 1, 0,
  634. 0, 0, 0, 0, 1
  635. ]);
  636. }
  637. function adjustBrightness(matrix, value) {
  638. value = clamp(255 * value, -255, 255);
  639. return multiply(matrix, [
  640. 1, 0, 0, 0, value,
  641. 0, 1, 0, 0, value,
  642. 0, 0, 1, 0, value,
  643. 0, 0, 0, 1, 0,
  644. 0, 0, 0, 0, 1
  645. ]);
  646. }
  647. function adjustColors(matrix, adjustR, adjustG, adjustB) {
  648. adjustR = clamp(adjustR, 0, 2);
  649. adjustG = clamp(adjustG, 0, 2);
  650. adjustB = clamp(adjustB, 0, 2);
  651. return multiply(matrix, [
  652. adjustR, 0, 0, 0, 0,
  653. 0, adjustG, 0, 0, 0,
  654. 0, 0, adjustB, 0, 0,
  655. 0, 0, 0, 1, 0,
  656. 0, 0, 0, 0, 1
  657. ]);
  658. }
  659. function adjustSepia(matrix, value) {
  660. value = clamp(value, 0, 1);
  661. return multiply(matrix, adjust([
  662. 0.393, 0.769, 0.189, 0, 0,
  663. 0.349, 0.686, 0.168, 0, 0,
  664. 0.272, 0.534, 0.131, 0, 0,
  665. 0, 0, 0, 1, 0,
  666. 0, 0, 0, 0, 1
  667. ], value));
  668. }
  669. function adjustGrayscale(matrix, value) {
  670. value = clamp(value, 0, 1);
  671. return multiply(matrix, adjust([
  672. 0.33, 0.34, 0.33, 0, 0,
  673. 0.33, 0.34, 0.33, 0, 0,
  674. 0.33, 0.34, 0.33, 0, 0,
  675. 0, 0, 0, 1, 0,
  676. 0, 0, 0, 0, 1
  677. ], value));
  678. }
  679. return {
  680. identity: identity,
  681. adjust: adjust,
  682. multiply: multiply,
  683. adjustContrast: adjustContrast,
  684. adjustBrightness: adjustBrightness,
  685. adjustSaturation: adjustSaturation,
  686. adjustHue: adjustHue,
  687. adjustColors: adjustColors,
  688. adjustSepia: adjustSepia,
  689. adjustGrayscale: adjustGrayscale
  690. };
  691. });
  692. /**
  693. * Filters.js
  694. *
  695. * Released under LGPL License.
  696. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  697. *
  698. * License: http://www.tinymce.com/license
  699. * Contributing: http://www.tinymce.com/contributing
  700. */
  701. /**
  702. * Applies various filters to blobs.
  703. */
  704. define("ephox/imagetools/transformations/Filters", [
  705. "ephox/imagetools/util/Canvas",
  706. "ephox/imagetools/util/ImageSize",
  707. "ephox/imagetools/util/Conversions",
  708. "ephox/imagetools/transformations/ColorMatrix"
  709. ], function(Canvas, ImageSize, Conversions, ColorMatrix) {
  710. var revokeImageUrl = Conversions.revokeImageUrl;
  711. function colorFilter(blob, matrix) {
  712. return Conversions.blobToImage(blob).then(function(image) {
  713. var canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image)),
  714. context = Canvas.get2dContext(canvas),
  715. pixels;
  716. function applyMatrix(pixels, m) {
  717. var d = pixels.data, r, g, b, a, i,
  718. m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4],
  719. m5 = m[5], m6 = m[6], m7 = m[7], m8 = m[8], m9 = m[9],
  720. m10 = m[10], m11 = m[11], m12 = m[12], m13 = m[13], m14 = m[14],
  721. m15 = m[15], m16 = m[16], m17 = m[17], m18 = m[18], m19 = m[19];
  722. for (i = 0; i < d.length; i += 4) {
  723. r = d[i];
  724. g = d[i + 1];
  725. b = d[i + 2];
  726. a = d[i + 3];
  727. d[i] = r * m0 + g * m1 + b * m2 + a * m3 + m4;
  728. d[i + 1] = r * m5 + g * m6 + b * m7 + a * m8 + m9;
  729. d[i + 2] = r * m10 + g * m11 + b * m12 + a * m13 + m14;
  730. d[i + 3] = r * m15 + g * m16 + b * m17 + a * m18 + m19;
  731. }
  732. return pixels;
  733. }
  734. context.drawImage(image, 0, 0);
  735. revokeImageUrl(image);
  736. pixels = applyMatrix(context.getImageData(0, 0, canvas.width, canvas.height), matrix);
  737. context.putImageData(pixels, 0, 0);
  738. return Conversions.canvasToBlob(canvas);
  739. });
  740. }
  741. function convoluteFilter(blob, matrix) {
  742. return Conversions.blobToImage(blob).then(function(image) {
  743. var canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image)),
  744. context = Canvas.get2dContext(canvas),
  745. pixelsIn, pixelsOut;
  746. function applyMatrix(pixelsIn, pixelsOut, matrix) {
  747. var rgba, drgba, side, halfSide, x, y, r, g, b,
  748. cx, cy, scx, scy, offset, wt, w, h;
  749. function clamp(value, min, max) {
  750. if (value > max) {
  751. value = max;
  752. } else if (value < min) {
  753. value = min;
  754. }
  755. return value;
  756. }
  757. // Calc side and half side of matrix
  758. side = Math.round(Math.sqrt(matrix.length));
  759. halfSide = Math.floor(side / 2);
  760. rgba = pixelsIn.data;
  761. drgba = pixelsOut.data;
  762. w = pixelsIn.width;
  763. h = pixelsIn.height;
  764. // Apply convolution matrix to pixels
  765. for (y = 0; y < h; y++) {
  766. for (x = 0; x < w; x++) {
  767. r = g = b = 0;
  768. for (cy = 0; cy < side; cy++) {
  769. for (cx = 0; cx < side; cx++) {
  770. // Calc relative x, y based on matrix
  771. scx = clamp(x + cx - halfSide, 0, w - 1);
  772. scy = clamp(y + cy - halfSide, 0, h - 1);
  773. // Calc r, g, b
  774. offset = (scy * w + scx) * 4;
  775. wt = matrix[cy * side + cx];
  776. r += rgba[offset] * wt;
  777. g += rgba[offset + 1] * wt;
  778. b += rgba[offset + 2] * wt;
  779. }
  780. }
  781. // Set new RGB to destination buffer
  782. offset = (y * w + x) * 4;
  783. drgba[offset] = clamp(r, 0, 255);
  784. drgba[offset + 1] = clamp(g, 0, 255);
  785. drgba[offset + 2] = clamp(b, 0, 255);
  786. }
  787. }
  788. return pixelsOut;
  789. }
  790. context.drawImage(image, 0, 0);
  791. revokeImageUrl(image);
  792. pixelsIn = context.getImageData(0, 0, canvas.width, canvas.height);
  793. pixelsOut = context.getImageData(0, 0, canvas.width, canvas.height);
  794. pixelsOut = applyMatrix(pixelsIn, pixelsOut, matrix);
  795. context.putImageData(pixelsOut, 0, 0);
  796. return Conversions.canvasToBlob(canvas);
  797. });
  798. }
  799. function functionColorFilter(colorFn) {
  800. return function(blob, value) {
  801. return Conversions.blobToImage(blob).then(function(image) {
  802. var canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image)),
  803. context = Canvas.get2dContext(canvas),
  804. pixels, i, lookup = new Array(256);
  805. function applyLookup(pixels, lookup) {
  806. var d = pixels.data, i;
  807. for (i = 0; i < d.length; i += 4) {
  808. d[i] = lookup[d[i]];
  809. d[i + 1] = lookup[d[i + 1]];
  810. d[i + 2] = lookup[d[i + 2]];
  811. }
  812. return pixels;
  813. }
  814. for (i = 0; i < lookup.length; i++) {
  815. lookup[i] = colorFn(i, value);
  816. }
  817. context.drawImage(image, 0, 0);
  818. revokeImageUrl(image);
  819. pixels = applyLookup(context.getImageData(0, 0, canvas.width, canvas.height), lookup);
  820. context.putImageData(pixels, 0, 0);
  821. return Conversions.canvasToBlob(canvas);
  822. });
  823. };
  824. }
  825. function complexAdjustableColorFilter(matrixAdjustFn) {
  826. return function(blob, adjust) {
  827. return colorFilter(blob, matrixAdjustFn(ColorMatrix.identity(), adjust));
  828. };
  829. }
  830. function basicColorFilter(matrix) {
  831. return function(blob) {
  832. return colorFilter(blob, matrix);
  833. };
  834. }
  835. function basicConvolutionFilter(kernel) {
  836. return function(blob) {
  837. return convoluteFilter(blob, kernel);
  838. };
  839. }
  840. return {
  841. invert: basicColorFilter([
  842. -1, 0, 0, 0, 255,
  843. 0, -1, 0, 0, 255,
  844. 0, 0, -1, 0, 255,
  845. 0, 0, 0, 1, 0
  846. ]),
  847. brightness: complexAdjustableColorFilter(ColorMatrix.adjustBrightness),
  848. hue: complexAdjustableColorFilter(ColorMatrix.adjustHue),
  849. saturate: complexAdjustableColorFilter(ColorMatrix.adjustSaturation),
  850. contrast: complexAdjustableColorFilter(ColorMatrix.adjustContrast),
  851. grayscale: complexAdjustableColorFilter(ColorMatrix.adjustGrayscale),
  852. sepia: complexAdjustableColorFilter(ColorMatrix.adjustSepia),
  853. colorize: function(blob, adjustR, adjustG, adjustB) {
  854. return colorFilter(blob, ColorMatrix.adjustColors(ColorMatrix.identity(), adjustR, adjustG, adjustB));
  855. },
  856. sharpen: basicConvolutionFilter([
  857. 0, -1, 0,
  858. -1, 5, -1,
  859. 0, -1, 0
  860. ]),
  861. emboss: basicConvolutionFilter([
  862. -2, -1, 0,
  863. -1, 1, 1,
  864. 0, 1, 2
  865. ]),
  866. gamma: functionColorFilter(function(color, value) {
  867. return Math.pow(color / 255, 1 - value) * 255;
  868. }),
  869. exposure: functionColorFilter(function(color, value) {
  870. return 255 * (1 - Math.exp(-(color / 255) * value));
  871. }),
  872. colorFilter: colorFilter,
  873. convoluteFilter: convoluteFilter
  874. };
  875. });
  876. /**
  877. * ImageTools.js
  878. *
  879. * Released under LGPL License.
  880. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  881. *
  882. * License: http://www.tinymce.com/license
  883. * Contributing: http://www.tinymce.com/contributing
  884. */
  885. /**
  886. * Modifies image blobs.
  887. */
  888. define("ephox/imagetools/transformations/ImageTools", [
  889. "ephox/imagetools/util/Conversions",
  890. "ephox/imagetools/util/Canvas",
  891. "ephox/imagetools/util/ImageSize"
  892. ], function(Conversions, Canvas, ImageSize) {
  893. var revokeImageUrl = Conversions.revokeImageUrl;
  894. function rotate(blob, angle) {
  895. return Conversions.blobToImage(blob).then(function(image) {
  896. var canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image)),
  897. context = Canvas.get2dContext(canvas),
  898. translateX = 0, translateY = 0;
  899. angle = angle < 0 ? 360 + angle : angle;
  900. if (angle == 90 || angle == 270) {
  901. Canvas.resize(canvas, canvas.height, canvas.width);
  902. }
  903. if (angle == 90 || angle == 180) {
  904. translateX = canvas.width;
  905. }
  906. if (angle == 270 || angle == 180) {
  907. translateY = canvas.height;
  908. }
  909. context.translate(translateX, translateY);
  910. context.rotate(angle * Math.PI / 180);
  911. context.drawImage(image, 0, 0);
  912. revokeImageUrl(image);
  913. return Conversions.canvasToBlob(canvas, blob.type);
  914. });
  915. }
  916. function flip(blob, axis) {
  917. return Conversions.blobToImage(blob).then(function(image) {
  918. var canvas = Canvas.create(ImageSize.getWidth(image), ImageSize.getHeight(image)),
  919. context = Canvas.get2dContext(canvas);
  920. if (axis == 'v') {
  921. context.scale(1, -1);
  922. context.drawImage(image, 0, -canvas.height);
  923. } else {
  924. context.scale(-1, 1);
  925. context.drawImage(image, -canvas.width, 0);
  926. }
  927. revokeImageUrl(image);
  928. return Conversions.canvasToBlob(canvas);
  929. });
  930. }
  931. function crop(blob, x, y, w, h) {
  932. return Conversions.blobToImage(blob).then(function(image) {
  933. var canvas = Canvas.create(w, h),
  934. context = Canvas.get2dContext(canvas);
  935. context.drawImage(image, -x, -y);
  936. revokeImageUrl(image);
  937. return Conversions.canvasToBlob(canvas);
  938. });
  939. }
  940. function resize(blob, w, h) {
  941. return Conversions.blobToImage(blob).then(function(image) {
  942. var canvas = Canvas.create(w, h),
  943. context = Canvas.get2dContext(canvas);
  944. context.drawImage(image, 0, 0, w, h);
  945. revokeImageUrl(image);
  946. return Conversions.canvasToBlob(canvas, blob.type);
  947. });
  948. }
  949. return {
  950. rotate: rotate,
  951. flip: flip,
  952. crop: crop,
  953. resize: resize
  954. };
  955. });
  956. define(
  957. 'ephox/imagetools/api/ImageTransformations',
  958. [
  959. 'ephox/imagetools/transformations/Filters',
  960. 'ephox/imagetools/transformations/ImageTools'
  961. ],
  962. function (Filters, ImageTools) {
  963. var invert = function (blob) {
  964. return Filters.invert(blob);
  965. };
  966. var sharpen = function (blob) {
  967. return Filters.sharpen(blob);
  968. };
  969. var emboss = function (blob) {
  970. return Filters.emboss(blob);
  971. };
  972. var gamma = function (blob, value) {
  973. return Filters.gamma(blob, value);
  974. };
  975. var exposure = function (blob, value) {
  976. return Filters.exposure(blob, value);
  977. };
  978. var colorize = function (blob, adjustR, adjustG, adjustB) {
  979. return Filters.colorize(blob, adjustR, adjustG, adjustB);
  980. };
  981. var brightness = function (blob, adjust) {
  982. return Filters.brightness(blob, adjust);
  983. };
  984. var hue = function (blob, adjust) {
  985. return Filters.hue(blob, adjust);
  986. };
  987. var saturate = function (blob, adjust) {
  988. return Filters.saturate(blob, adjust);
  989. };
  990. var contrast = function (blob, adjust) {
  991. return Filters.contrast(blob, adjust);
  992. };
  993. var grayscale = function (blob, adjust) {
  994. return Filters.grayscale(blob, adjust);
  995. };
  996. var sepia = function (blob, adjust) {
  997. return Filters.sepia(blob, adjust);
  998. };
  999. var flip = function (blob, axis) {
  1000. return ImageTools.flip(blob, axis);
  1001. };
  1002. var crop = function (blob, x, y, w, h) {
  1003. return ImageTools.crop(blob, x, y, w, h);
  1004. };
  1005. var resize = function (blob, w, h) {
  1006. return ImageTools.resize(blob, w, h);
  1007. };
  1008. var rotate = function (blob, angle) {
  1009. return ImageTools.rotate(blob, angle);
  1010. };
  1011. return {
  1012. invert: invert,
  1013. sharpen: sharpen,
  1014. emboss: emboss,
  1015. brightness: brightness,
  1016. hue: hue,
  1017. saturate: saturate,
  1018. contrast: contrast,
  1019. grayscale: grayscale,
  1020. sepia: sepia,
  1021. colorize: colorize,
  1022. gamma: gamma,
  1023. exposure: exposure,
  1024. flip: flip,
  1025. crop: crop,
  1026. resize: resize,
  1027. rotate: rotate
  1028. };
  1029. }
  1030. );
  1031. define(
  1032. 'ephox/imagetools/api/BlobConversions',
  1033. [
  1034. 'ephox/imagetools/util/Conversions'
  1035. ],
  1036. function (Conversions) {
  1037. var blobToImage = function (image) {
  1038. return Conversions.blobToImage(image);
  1039. };
  1040. var imageToBlob = function (blob) {
  1041. return Conversions.imageToBlob(blob);
  1042. };
  1043. var blobToDataUri = function (blob) {
  1044. return Conversions.blobToDataUri(blob);
  1045. };
  1046. var blobToBase64 = function (blob) {
  1047. return Conversions.blobToBase64(blob);
  1048. };
  1049. return {
  1050. // used outside
  1051. blobToImage: blobToImage,
  1052. // used outside
  1053. imageToBlob: imageToBlob,
  1054. // used outside
  1055. blobToDataUri: blobToDataUri,
  1056. // used outside
  1057. blobToBase64: blobToBase64
  1058. };
  1059. }
  1060. );
  1061. defineGlobal("global!tinymce.dom.DOMUtils", tinymce.dom.DOMUtils);
  1062. defineGlobal("global!tinymce.ui.Factory", tinymce.ui.Factory);
  1063. defineGlobal("global!tinymce.ui.Form", tinymce.ui.Form);
  1064. defineGlobal("global!tinymce.ui.Container", tinymce.ui.Container);
  1065. defineGlobal("global!tinymce.ui.Control", tinymce.ui.Control);
  1066. defineGlobal("global!tinymce.ui.DragHelper", tinymce.ui.DragHelper);
  1067. defineGlobal("global!tinymce.geom.Rect", tinymce.geom.Rect);
  1068. defineGlobal("global!tinymce.dom.DomQuery", tinymce.dom.DomQuery);
  1069. defineGlobal("global!tinymce.util.Observable", tinymce.util.Observable);
  1070. defineGlobal("global!tinymce.util.VK", tinymce.util.VK);
  1071. /**
  1072. * CropRect.js
  1073. *
  1074. * Released under LGPL License.
  1075. * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
  1076. *
  1077. * License: http://www.tinymce.com/license
  1078. * Contributing: http://www.tinymce.com/contributing
  1079. */
  1080. /**
  1081. * ...
  1082. */
  1083. define("tinymce/imagetoolsplugin/CropRect", [
  1084. "global!tinymce.dom.DomQuery",
  1085. "global!tinymce.ui.DragHelper",
  1086. "global!tinymce.geom.Rect",
  1087. "global!tinymce.util.Tools",
  1088. "global!tinymce.util.Observable",
  1089. "global!tinymce.util.VK"
  1090. ], function($, DragHelper, Rect, Tools, Observable, VK) {
  1091. var count = 0;
  1092. return function(currentRect, viewPortRect, clampRect, containerElm, action) {
  1093. var instance, handles, dragHelpers, blockers, prefix = 'mce-', id = prefix + 'crid-' + (count++);
  1094. handles = [
  1095. {name: 'move', xMul: 0, yMul: 0, deltaX: 1, deltaY: 1, deltaW: 0, deltaH: 0, label: 'Crop Mask'},
  1096. {name: 'nw', xMul: 0, yMul: 0, deltaX: 1, deltaY: 1, deltaW: -1, deltaH: -1, label: 'Top Left Crop Handle'},
  1097. {name: 'ne', xMul: 1, yMul: 0, deltaX: 0, deltaY: 1, deltaW: 1, deltaH: -1, label: 'Top Right Crop Handle'},
  1098. {name: 'sw', xMul: 0, yMul: 1, deltaX: 1, deltaY: 0, deltaW: -1, deltaH: 1, label: 'Bottom Left Crop Handle'},
  1099. {name: 'se', xMul: 1, yMul: 1, deltaX: 0, deltaY: 0, deltaW: 1, deltaH: 1, label: 'Bottom Right Crop Handle'}
  1100. ];
  1101. blockers = ["top", "right", "bottom", "left"];
  1102. function getAbsoluteRect(outerRect, relativeRect) {
  1103. return {
  1104. x: relativeRect.x + outerRect.x,
  1105. y: relativeRect.y + outerRect.y,
  1106. w: relativeRect.w,
  1107. h: relativeRect.h
  1108. };
  1109. }
  1110. function getRelativeRect(outerRect, innerRect) {
  1111. return {
  1112. x: innerRect.x - outerRect.x,
  1113. y: innerRect.y - outerRect.y,
  1114. w: innerRect.w,
  1115. h: innerRect.h
  1116. };
  1117. }
  1118. function getInnerRect() {
  1119. return getRelativeRect(clampRect, currentRect);
  1120. }
  1121. function moveRect(handle, startRect, deltaX, deltaY) {
  1122. var x, y, w, h, rect;
  1123. x = startRect.x;
  1124. y = startRect.y;
  1125. w = startRect.w;
  1126. h = startRect.h;
  1127. x += deltaX * handle.deltaX;
  1128. y += deltaY * handle.deltaY;
  1129. w += deltaX * handle.deltaW;
  1130. h += deltaY * handle.deltaH;
  1131. if (w < 20) {
  1132. w = 20;
  1133. }
  1134. if (h < 20) {
  1135. h = 20;
  1136. }
  1137. rect = currentRect = Rect.clamp({x: x, y: y, w: w, h: h}, clampRect, handle.name == 'move');
  1138. rect = getRelativeRect(clampRect, rect);
  1139. instance.fire('updateRect', {rect: rect});
  1140. setInnerRect(rect);
  1141. }
  1142. function render() {
  1143. function createDragHelper(handle) {
  1144. var startRect;
  1145. return new DragHelper(id, {
  1146. document: containerElm.ownerDocument,
  1147. handle: id + '-' + handle.name,
  1148. start: function() {
  1149. startRect = currentRect;
  1150. },
  1151. drag: function(e) {
  1152. moveRect(handle, startRect, e.deltaX, e.deltaY);
  1153. }
  1154. });
  1155. }
  1156. $(
  1157. '<div id="' + id + '" class="' + prefix + 'croprect-container"' +
  1158. ' role="grid" aria-dropeffect="execute">'
  1159. ).appendTo(containerElm);
  1160. Tools.each(blockers, function(blocker) {
  1161. $('#' + id, containerElm).append(
  1162. '<div id="' + id + '-' + blocker + '"class="' + prefix + 'croprect-block" style="display: none" data-mce-bogus="all">'
  1163. );
  1164. });
  1165. Tools.each(handles, function(handle) {
  1166. $('#' + id, containerElm).append(
  1167. '<div id="' + id + '-' + handle.name + '" class="' + prefix +
  1168. 'croprect-handle ' + prefix + 'croprect-handle-' + handle.name + '"' +
  1169. 'style="display: none" data-mce-bogus="all" role="gridcell" tabindex="-1"' +
  1170. ' aria-label="' + handle.label + '" aria-grabbed="false">'
  1171. );
  1172. });
  1173. dragHelpers = Tools.map(handles, createDragHelper);
  1174. repaint(currentRect);
  1175. $(containerElm).on('focusin focusout', function(e) {
  1176. $(e.target).attr('aria-grabbed', e.type === 'focus');
  1177. });
  1178. $(containerElm).on('keydown', function(e) {
  1179. var activeHandle;
  1180. Tools.each(handles, function(handle) {
  1181. if (e.target.id == id + '-' + handle.name) {
  1182. activeHandle = handle;
  1183. return false;
  1184. }
  1185. });
  1186. function moveAndBlock(evt, handle, startRect, deltaX, deltaY) {
  1187. evt.stopPropagation();
  1188. evt.preventDefault();
  1189. moveRect(activeHandle, startRect, deltaX, deltaY);
  1190. }
  1191. switch (e.keyCode) {
  1192. case VK.LEFT:
  1193. moveAndBlock(e, activeHandle, currentRect, -10, 0);
  1194. break;
  1195. case VK.RIGHT:
  1196. moveAndBlock(e, activeHandle, currentRect, 10, 0);
  1197. break;
  1198. case VK.UP:
  1199. moveAndBlock(e, activeHandle, currentRect, 0, -10);
  1200. break;
  1201. case VK.DOWN:
  1202. moveAndBlock(e, activeHandle, currentRect, 0, 10);
  1203. break;
  1204. case VK.ENTER:
  1205. case VK.SPACEBAR:
  1206. e.preventDefault();
  1207. action();
  1208. break;
  1209. }
  1210. });
  1211. }
  1212. function toggleVisibility(state) {
  1213. var selectors;
  1214. selectors = Tools.map(handles, function(handle) {
  1215. return '#' + id + '-' + handle.name;
  1216. }).concat(Tools.map(blockers, function(blocker) {
  1217. return '#' + id + '-' + blocker;
  1218. })).join(',');
  1219. if (state) {
  1220. $(selectors, containerElm).show();
  1221. } else {
  1222. $(selectors, containerElm).hide();
  1223. }
  1224. }
  1225. function repaint(rect) {
  1226. function updateElementRect(name, rect) {
  1227. if (rect.h < 0) {
  1228. rect.h = 0;
  1229. }
  1230. if (rect.w < 0) {
  1231. rect.w = 0;
  1232. }
  1233. $('#' + id + '-' + name, containerElm).css({
  1234. left: rect.x,
  1235. top: rect.y,
  1236. width: rect.w,
  1237. height: rect.h
  1238. });
  1239. }
  1240. Tools.each(handles, function(handle) {
  1241. $('#' + id + '-' + handle.name, containerElm).css({
  1242. left: rect.w * handle.xMul + rect.x,
  1243. top: rect.h * handle.yMul + rect.y
  1244. });
  1245. });
  1246. updateElementRect('top', {x: viewPortRect.x, y: viewPortRect.y, w: viewPortRect.w, h: rect.y - viewPortRect.y});
  1247. updateElementRect('right', {x: rect.x + rect.w, y: rect.y, w: viewPortRect.w - rect.x - rect.w + viewPortRect.x, h: rect.h});
  1248. updateElementRect('bottom', {
  1249. x: viewPortRect.x,
  1250. y: rect.y + rect.h,
  1251. w: viewPortRect.w,
  1252. h: viewPortRect.h - rect.y - rect.h + viewPortRect.y
  1253. });
  1254. updateElementRect('left', {x: viewPortRect.x, y: rect.y, w: rect.x - viewPortRect.x, h: rect.h});
  1255. updateElementRect('move', rect);
  1256. }
  1257. function setRect(rect) {
  1258. currentRect = rect;
  1259. repaint(currentRect);
  1260. }
  1261. function setViewPortRect(rect) {
  1262. viewPortRect = rect;
  1263. repaint(currentRect);
  1264. }
  1265. function setInnerRect(rect) {
  1266. setRect(getAbsoluteRect(clampRect, rect));
  1267. }
  1268. function setClampRect(rect) {
  1269. clampRect = rect;
  1270. repaint(currentRect);
  1271. }
  1272. function destroy() {
  1273. Tools.each(dragHelpers, function(helper) {
  1274. helper.destroy();
  1275. });
  1276. dragHelpers = [];
  1277. }
  1278. render(containerElm);
  1279. instance = Tools.extend({
  1280. toggleVisibility: toggleVisibility,
  1281. setClampRect: setClampRect,
  1282. setRect: setRect,
  1283. getInnerRect: getInnerRect,
  1284. setInnerRect: setInnerRect,
  1285. setViewPortRect: setViewPortRect,
  1286. destroy: destroy
  1287. }, Observable);
  1288. return instance;
  1289. };
  1290. });
  1291. /**
  1292. * ImagePanel.js
  1293. *
  1294. * Released under LGPL License.
  1295. * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
  1296. *
  1297. * License: http://www.tinymce.com/license
  1298. * Contributing: http://www.tinymce.com/contributing
  1299. */
  1300. /**
  1301. * ...
  1302. *
  1303. * @-x-less ImagePanel.less
  1304. */
  1305. define("tinymce/imagetoolsplugin/ImagePanel", [
  1306. "global!tinymce.ui.Control",
  1307. "global!tinymce.ui.DragHelper",
  1308. "global!tinymce.geom.Rect",
  1309. "global!tinymce.util.Tools",
  1310. "global!tinymce.util.Promise",
  1311. "tinymce/imagetoolsplugin/CropRect"
  1312. ], function(Control, DragHelper, Rect, Tools, Promise, CropRect) {
  1313. function loadImage(image) {
  1314. return new Promise(function(resolve) {
  1315. function loaded() {
  1316. image.removeEventListener('load', loaded);
  1317. resolve(image);
  1318. }
  1319. if (image.complete) {
  1320. resolve(image);
  1321. } else {
  1322. image.addEventListener('load', loaded);
  1323. }
  1324. });
  1325. }
  1326. return Control.extend({
  1327. Defaults: {
  1328. classes: "imagepanel"
  1329. },
  1330. selection: function(rect) {
  1331. if (arguments.length) {
  1332. this.state.set('rect', rect);
  1333. return this;
  1334. }
  1335. return this.state.get('rect');
  1336. },
  1337. imageSize: function() {
  1338. var viewRect = this.state.get('viewRect');
  1339. return {
  1340. w: viewRect.w,
  1341. h: viewRect.h
  1342. };
  1343. },
  1344. toggleCropRect: function(state) {
  1345. this.state.set('cropEnabled', state);
  1346. },
  1347. imageSrc: function(url) {
  1348. var self = this, img = new Image();
  1349. img.src = url;
  1350. loadImage(img).then(function() {
  1351. var rect, $img, lastRect = self.state.get('viewRect');
  1352. $img = self.$el.find('img');
  1353. if ($img[0]) {
  1354. $img.replaceWith(img);
  1355. } else {
  1356. self.getEl().appendChild(img);
  1357. }
  1358. rect = {x: 0, y: 0, w: img.naturalWidth, h: img.naturalHeight};
  1359. self.state.set('viewRect', rect);
  1360. self.state.set('rect', Rect.inflate(rect, -20, -20));
  1361. if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) {
  1362. self.zoomFit();
  1363. }
  1364. self.repaintImage();
  1365. self.fire('load');
  1366. });
  1367. },
  1368. zoom: function(value) {
  1369. if (arguments.length) {
  1370. this.state.set('zoom', value);
  1371. return this;
  1372. }
  1373. return this.state.get('zoom');
  1374. },
  1375. postRender: function() {
  1376. this.imageSrc(this.settings.imageSrc);
  1377. return this._super();
  1378. },
  1379. zoomFit: function() {
  1380. var self = this, $img, pw, ph, w, h, zoom, padding;
  1381. padding = 10;
  1382. $img = self.$el.find('img');
  1383. pw = self.getEl().clientWidth;
  1384. ph = self.getEl().clientHeight;
  1385. w = $img[0].naturalWidth;
  1386. h = $img[0].naturalHeight;
  1387. zoom = Math.min((pw - padding) / w, (ph - padding) / h);
  1388. if (zoom >= 1) {
  1389. zoom = 1;
  1390. }
  1391. self.zoom(zoom);
  1392. },
  1393. repaintImage: function() {
  1394. var x, y, w, h, pw, ph, $img, zoom, rect, elm;
  1395. elm = this.getEl();
  1396. zoom = this.zoom();
  1397. rect = this.state.get('rect');
  1398. $img = this.$el.find('img');
  1399. pw = elm.offsetWidth;
  1400. ph = elm.offsetHeight;
  1401. w = $img[0].naturalWidth * zoom;
  1402. h = $img[0].naturalHeight * zoom;
  1403. x = Math.max(0, pw / 2 - w / 2);
  1404. y = Math.max(0, ph / 2 - h / 2);
  1405. $img.css({
  1406. left: x,
  1407. top: y,
  1408. width: w,
  1409. height: h
  1410. });
  1411. if (this.cropRect) {
  1412. this.cropRect.setRect({
  1413. x: rect.x * zoom + x,
  1414. y: rect.y * zoom + y,
  1415. w: rect.w * zoom,
  1416. h: rect.h * zoom
  1417. });
  1418. this.cropRect.setClampRect({
  1419. x: x,
  1420. y: y,
  1421. w: w,
  1422. h: h
  1423. });
  1424. this.cropRect.setViewPortRect({
  1425. x: 0,
  1426. y: 0,
  1427. w: pw,
  1428. h: ph
  1429. });
  1430. }
  1431. },
  1432. bindStates: function() {
  1433. var self = this;
  1434. function setupCropRect(rect) {
  1435. self.cropRect = new CropRect(
  1436. rect,
  1437. self.state.get('viewRect'),
  1438. self.state.get('viewRect'),
  1439. self.getEl(),
  1440. function() {
  1441. self.fire('crop');
  1442. }
  1443. );
  1444. self.cropRect.on('updateRect', function(e) {
  1445. var rect = e.rect, zoom = self.zoom();
  1446. rect = {
  1447. x: Math.round(rect.x / zoom),
  1448. y: Math.round(rect.y / zoom),
  1449. w: Math.round(rect.w / zoom),
  1450. h: Math.round(rect.h / zoom)
  1451. };
  1452. self.state.set('rect', rect);
  1453. });
  1454. self.on('remove', self.cropRect.destroy);
  1455. }
  1456. self.state.on('change:cropEnabled', function(e) {
  1457. self.cropRect.toggleVisibility(e.value);
  1458. self.repaintImage();
  1459. });
  1460. self.state.on('change:zoom', function() {
  1461. self.repaintImage();
  1462. });
  1463. self.state.on('change:rect', function(e) {
  1464. var rect = e.value;
  1465. if (!self.cropRect) {
  1466. setupCropRect(rect);
  1467. }
  1468. self.cropRect.setRect(rect);
  1469. });
  1470. }
  1471. });
  1472. });
  1473. /**
  1474. * UndoStack.js
  1475. *
  1476. * Released under LGPL License.
  1477. * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
  1478. *
  1479. * License: http://www.tinymce.com/license
  1480. * Contributing: http://www.tinymce.com/contributing
  1481. */
  1482. define("tinymce/imagetoolsplugin/UndoStack", [
  1483. ], function() {
  1484. return function() {
  1485. var data = [], index = -1;
  1486. function add(state) {
  1487. var removed;
  1488. removed = data.splice(++index);
  1489. data.push(state);
  1490. return {
  1491. state: state,
  1492. removed: removed
  1493. };
  1494. }
  1495. function undo() {
  1496. if (canUndo()) {
  1497. return data[--index];
  1498. }
  1499. }
  1500. function redo() {
  1501. if (canRedo()) {
  1502. return data[++index];
  1503. }
  1504. }
  1505. function canUndo() {
  1506. return index > 0;
  1507. }
  1508. function canRedo() {
  1509. return index != -1 && index < data.length - 1;
  1510. }
  1511. return {
  1512. data: data,
  1513. add: add,
  1514. undo: undo,
  1515. redo: redo,
  1516. canUndo: canUndo,
  1517. canRedo: canRedo
  1518. };
  1519. };
  1520. });
  1521. /**
  1522. * Dialog.js
  1523. *
  1524. * Released under LGPL License.
  1525. * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
  1526. *
  1527. * License: http://www.tinymce.com/license
  1528. * Contributing: http://www.tinymce.com/contributing
  1529. */
  1530. /**
  1531. * ...
  1532. */
  1533. define("tinymce/imagetoolsplugin/Dialog", [
  1534. "global!tinymce.dom.DOMUtils",
  1535. "global!tinymce.util.Tools",
  1536. "global!tinymce.util.Promise",
  1537. "global!tinymce.ui.Factory",
  1538. "global!tinymce.ui.Form",
  1539. "global!tinymce.ui.Container",
  1540. "tinymce/imagetoolsplugin/ImagePanel",
  1541. "ephox/imagetools/api/ImageTransformations",
  1542. "ephox/imagetools/api/BlobConversions",
  1543. "tinymce/imagetoolsplugin/UndoStack"
  1544. ], function(DOMUtils, Tools, Promise, Factory, Form, Container, ImagePanel, ImageTransformations, BlobConversions, UndoStack) {
  1545. function createState(blob) {
  1546. return {
  1547. blob: blob,
  1548. url: URL.createObjectURL(blob)
  1549. };
  1550. }
  1551. function destroyState(state) {
  1552. if (state) {
  1553. URL.revokeObjectURL(state.url);
  1554. }
  1555. }
  1556. function destroyStates(states) {
  1557. Tools.each(states, destroyState);
  1558. }
  1559. function open(currentState, resolve, reject) {
  1560. var win, undoStack = new UndoStack(), mainPanel, filtersPanel, tempState,
  1561. cropPanel, resizePanel, flipRotatePanel, imagePanel, sidePanel, mainViewContainer,
  1562. invertPanel, brightnessPanel, huePanel, saturatePanel, contrastPanel, grayscalePanel,
  1563. sepiaPanel, colorizePanel, sharpenPanel, embossPanel, gammaPanel, exposurePanel, panels,
  1564. width, height, ratioW, ratioH;
  1565. function recalcSize(e) {
  1566. var widthCtrl, heightCtrl, newWidth, newHeight;
  1567. widthCtrl = win.find('#w')[0];
  1568. heightCtrl = win.find('#h')[0];
  1569. newWidth = parseInt(widthCtrl.value(), 10);
  1570. newHeight = parseInt(heightCtrl.value(), 10);
  1571. if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
  1572. if (e.control.settings.name == 'w') {
  1573. newHeight = Math.round(newWidth * ratioW);
  1574. heightCtrl.value(newHeight);
  1575. } else {
  1576. newWidth = Math.round(newHeight * ratioH);
  1577. widthCtrl.value(newWidth);
  1578. }
  1579. }
  1580. width = newWidth;
  1581. height = newHeight;
  1582. }
  1583. function floatToPercent(value) {
  1584. return Math.round(value * 100) + '%';
  1585. }
  1586. function updateButtonUndoStates() {
  1587. win.find('#undo').disabled(!undoStack.canUndo());
  1588. win.find('#redo').disabled(!undoStack.canRedo());
  1589. win.statusbar.find('#save').disabled(!undoStack.canUndo());
  1590. }
  1591. function disableUndoRedo() {
  1592. win.find('#undo').disabled(true);
  1593. win.find('#redo').disabled(true);
  1594. }
  1595. function displayState(state) {
  1596. if (state) {
  1597. imagePanel.imageSrc(state.url);
  1598. }
  1599. }
  1600. function switchPanel(targetPanel) {
  1601. return function() {
  1602. var hidePanels = Tools.grep(panels, function(panel) {
  1603. return panel.settings.name != targetPanel;
  1604. });
  1605. Tools.each(hidePanels, function(panel) {
  1606. panel.hide();
  1607. });
  1608. targetPanel.show();
  1609. targetPanel.focus();
  1610. };
  1611. }
  1612. function addTempState(blob) {
  1613. tempState = createState(blob);
  1614. displayState(tempState);
  1615. }
  1616. function addBlobState(blob) {
  1617. currentState = createState(blob);
  1618. displayState(currentState);
  1619. destroyStates(undoStack.add(currentState).removed);
  1620. updateButtonUndoStates();
  1621. }
  1622. function crop() {
  1623. var rect = imagePanel.selection();
  1624. ImageTransformations.crop(currentState.blob, rect.x, rect.y, rect.w, rect.h).then(function(blob) {
  1625. addBlobState(blob);
  1626. cancel();
  1627. });
  1628. }
  1629. function tempAction(fn) {
  1630. var args = [].slice.call(arguments, 1);
  1631. return function() {
  1632. var state = tempState || currentState;
  1633. fn.apply(this, [state.blob].concat(args)).then(addTempState);
  1634. };
  1635. }
  1636. function action(fn) {
  1637. var args = [].slice.call(arguments, 1);
  1638. return function() {
  1639. fn.apply(this, [currentState.blob].concat(args)).then(addBlobState);
  1640. };
  1641. }
  1642. function cancel() {
  1643. displayState(currentState);
  1644. destroyState(tempState);
  1645. switchPanel(mainPanel)();
  1646. updateButtonUndoStates(

Large files files are truncated, but you can click here to view the full file