mobile.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /**
  2. * mobile.js
  3. * @author NHN Ent. FE Development Team <dl_javascript@nhnent.com>
  4. * @fileoverview
  5. */
  6. /* eslint-disable vars-on-top */
  7. 'use strict';
  8. var MAX_RESOLUTION = 3264 * 2448; // 8MP (Mega Pixel)
  9. var supportingFileAPI = !!(window.File && window.FileList && window.FileReader);
  10. var rImageType = /data:(image\/.+);base64,/;
  11. var shapeOpt = {
  12. fill: '#fff',
  13. stroke: '#000',
  14. strokeWidth: 10
  15. };
  16. var activeObjectId;
  17. // Selector of image editor controls
  18. var submenuClass = '.submenu';
  19. var hiddenmenuClass = '.hiddenmenu';
  20. var $controls = $('.tui-image-editor-controls');
  21. var $menuButtons = $controls.find('.menu-button');
  22. var $submenuButtons = $controls.find('.submenu-button');
  23. var $btnShowMenu = $controls.find('.btn-prev');
  24. var $msg = $controls.find('.msg');
  25. var $subMenus = $controls.find(submenuClass);
  26. var $hiddenMenus = $controls.find(hiddenmenuClass);
  27. // Image editor controls - top menu buttons
  28. var $inputImage = $('#input-image-file');
  29. var $btnDownload = $('#btn-download');
  30. var $btnUndo = $('#btn-undo');
  31. var $btnRedo = $('#btn-redo');
  32. var $btnRemoveActiveObject = $('#btn-remove-active-object');
  33. // Image editor controls - bottom menu buttons
  34. var $btnCrop = $('#btn-crop');
  35. var $btnAddText = $('#btn-add-text');
  36. // Image editor controls - bottom submenu buttons
  37. var $btnApplyCrop = $('#btn-apply-crop');
  38. var $btnFlipX = $('#btn-flip-x');
  39. var $btnFlipY = $('#btn-flip-y');
  40. var $btnRotateClockwise = $('#btn-rotate-clockwise');
  41. var $btnRotateCounterClockWise = $('#btn-rotate-counter-clockwise');
  42. var $btnAddArrowIcon = $('#btn-add-arrow-icon');
  43. var $btnAddCancelIcon = $('#btn-add-cancel-icon');
  44. var $btnAddCustomIcon = $('#btn-add-custom-icon');
  45. var $btnFreeDrawing = $('#btn-free-drawing');
  46. var $btnLineDrawing = $('#btn-line-drawing');
  47. var $btnAddRect = $('#btn-add-rect');
  48. var $btnAddSquare = $('#btn-add-square');
  49. var $btnAddEllipse = $('#btn-add-ellipse');
  50. var $btnAddCircle = $('#btn-add-circle');
  51. var $btnAddTriangle = $('#btn-add-triangle');
  52. var $btnChangeTextStyle = $('.btn-change-text-style');
  53. // Image editor controls - etc.
  54. var $inputTextSizeRange = $('#input-text-size-range');
  55. var $inputBrushWidthRange = $('#input-brush-range');
  56. var $inputStrokeWidthRange = $('#input-stroke-range');
  57. var $inputCheckTransparent = $('#input-check-transparent');
  58. // Colorpicker
  59. var iconColorpicker = tui.component.colorpicker.create({
  60. container: $('#tui-icon-color-picker')[0],
  61. color: '#000000'
  62. });
  63. var textColorpicker = tui.component.colorpicker.create({
  64. container: $('#tui-text-color-picker')[0],
  65. color: '#000000'
  66. });
  67. var brushColorpicker = tui.component.colorpicker.create({
  68. container: $('#tui-brush-color-picker')[0],
  69. color: '#000000'
  70. });
  71. var shapeColorpicker = tui.component.colorpicker.create({
  72. container: $('#tui-shape-color-picker')[0],
  73. color: '#000000'
  74. });
  75. // Create image editor
  76. var imageEditor = new tui.ImageEditor('.tui-image-editor', {
  77. cssMaxWidth: document.documentElement.clientWidth,
  78. cssMaxHeight: document.documentElement.clientHeight,
  79. selectionStyle: {
  80. cornerSize: 50,
  81. rotatingPointOffset: 100
  82. }
  83. });
  84. var $displayingSubMenu, $displayingHiddenMenu;
  85. function hexToRGBa(hex, alpha) {
  86. var r = parseInt(hex.slice(1, 3), 16);
  87. var g = parseInt(hex.slice(3, 5), 16);
  88. var b = parseInt(hex.slice(5, 7), 16);
  89. var a = alpha || 1;
  90. return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
  91. }
  92. function base64ToBlob(data) {
  93. var mimeString = '';
  94. var raw, uInt8Array, i, rawLength;
  95. raw = data.replace(rImageType, function(header, imageType) {
  96. mimeString = imageType;
  97. return '';
  98. });
  99. raw = atob(raw);
  100. rawLength = raw.length;
  101. uInt8Array = new Uint8Array(rawLength); // eslint-disable-line
  102. for (i = 0; i < rawLength; i += 1) {
  103. uInt8Array[i] = raw.charCodeAt(i);
  104. }
  105. return new Blob([uInt8Array], {type: mimeString});
  106. }
  107. function getBrushSettings() {
  108. var brushWidth = $inputBrushWidthRange.val();
  109. var brushColor = brushColorpicker.getColor();
  110. return {
  111. width: brushWidth,
  112. color: hexToRGBa(brushColor, 0.5)
  113. };
  114. }
  115. function activateShapeMode() {
  116. imageEditor.stopDrawingMode();
  117. }
  118. function activateIconMode() {
  119. imageEditor.stopDrawingMode();
  120. }
  121. function activateTextMode() {
  122. if (imageEditor.getDrawingMode() !== 'TEXT') {
  123. imageEditor.stopDrawingMode();
  124. imageEditor.startDrawingMode('TEXT');
  125. }
  126. }
  127. function setTextToolbar(obj) {
  128. var fontSize = obj.fontSize;
  129. var fontColor = obj.fill;
  130. $inputTextSizeRange.val(fontSize);
  131. textColorpicker.setColor(fontColor);
  132. }
  133. function setIconToolbar(obj) {
  134. var iconColor = obj.fill;
  135. iconColorpicker.setColor(iconColor);
  136. }
  137. function setShapeToolbar(obj) {
  138. var strokeColor, fillColor, isTransparent;
  139. var colorType = $('[name="select-color-type"]:checked').val();
  140. if (colorType === 'stroke') {
  141. strokeColor = obj.stroke;
  142. isTransparent = (strokeColor === 'transparent');
  143. if (!isTransparent) {
  144. shapeColorpicker.setColor(strokeColor);
  145. }
  146. } else if (colorType === 'fill') {
  147. fillColor = obj.fill;
  148. isTransparent = (fillColor === 'transparent');
  149. if (!isTransparent) {
  150. shapeColorpicker.setColor(fillColor);
  151. }
  152. }
  153. $inputCheckTransparent.prop('checked', isTransparent);
  154. $inputStrokeWidthRange.val(obj.strokeWith);
  155. }
  156. function showSubMenu(type) {
  157. var index;
  158. switch (type) {
  159. case 'shape':
  160. index = 3;
  161. break;
  162. case 'icon':
  163. index = 4;
  164. break;
  165. case 'text':
  166. index = 5;
  167. break;
  168. default:
  169. index = 0;
  170. }
  171. $displayingSubMenu.hide();
  172. $displayingHiddenMenu.hide();
  173. $displayingSubMenu = $menuButtons.eq(index).parent().find(submenuClass).show();
  174. }
  175. // Bind custom event of image editor
  176. imageEditor.on({
  177. undoStackChanged: function(length) {
  178. if (length) {
  179. $btnUndo.removeClass('disabled');
  180. } else {
  181. $btnUndo.addClass('disabled');
  182. }
  183. },
  184. redoStackChanged: function(length) {
  185. if (length) {
  186. $btnRedo.removeClass('disabled');
  187. } else {
  188. $btnRedo.addClass('disabled');
  189. }
  190. },
  191. objectScaled: function(obj) {
  192. if (obj.type === 'text') {
  193. $inputTextSizeRange.val(obj.fontSize);
  194. }
  195. },
  196. objectActivated: function(obj) {
  197. activeObjectId = obj.id;
  198. if (obj.type === 'rect' || obj.type === 'circle' || obj.type === 'triangle') {
  199. showSubMenu('shape');
  200. setShapeToolbar(obj);
  201. activateShapeMode();
  202. } else if (obj.type === 'icon') {
  203. showSubMenu('icon');
  204. setIconToolbar(obj);
  205. activateIconMode();
  206. } else if (obj.type === 'text') {
  207. showSubMenu('text');
  208. setTextToolbar(obj);
  209. activateTextMode();
  210. }
  211. }
  212. });
  213. // Image editor controls action
  214. $menuButtons.on('click', function() {
  215. $displayingSubMenu = $(this).parent().find(submenuClass).show();
  216. $displayingHiddenMenu = $(this).parent().find(hiddenmenuClass);
  217. });
  218. $submenuButtons.on('click', function() {
  219. $displayingHiddenMenu.hide();
  220. $displayingHiddenMenu = $(this).parent().find(hiddenmenuClass).show();
  221. });
  222. $btnShowMenu.on('click', function() {
  223. $displayingSubMenu.hide();
  224. $displayingHiddenMenu.hide();
  225. $msg.show();
  226. imageEditor.stopDrawingMode();
  227. });
  228. //Image load action
  229. $inputImage.on('change', function(event) {
  230. var file;
  231. var img;
  232. var resolution;
  233. if (!supportingFileAPI) {
  234. alert('This browser does not support file-api');
  235. }
  236. file = event.target.files[0];
  237. if (file) {
  238. img = new Image();
  239. img.onload = function() {
  240. resolution = this.width * this.height;
  241. if (resolution <= MAX_RESOLUTION) {
  242. imageEditor.loadImageFromFile(file).then(() => {
  243. imageEditor.clearUndoStack();
  244. });
  245. } else {
  246. alert('Loaded image\'s resolution is too large!\nRecommended resolution is 3264 * 2448!');
  247. }
  248. URL.revokeObjectURL(file);
  249. };
  250. img.src = URL.createObjectURL(file);
  251. }
  252. });
  253. // Undo action
  254. $btnUndo.on('click', function() {
  255. if (!$(this).hasClass('disabled')) {
  256. imageEditor.undo();
  257. }
  258. });
  259. // Redo action
  260. $btnRedo.on('click', function() {
  261. if (!$(this).hasClass('disabled')) {
  262. imageEditor.redo();
  263. }
  264. });
  265. // Remove active object action
  266. $btnRemoveActiveObject.on('click', function() {
  267. imageEditor.removeObject(activeObjectId);
  268. });
  269. // Download action
  270. $btnDownload.on('click', function() {
  271. var imageName = imageEditor.getImageName();
  272. var dataURL = imageEditor.toDataURL();
  273. var blob, type, w;
  274. if (supportingFileAPI) {
  275. blob = base64ToBlob(dataURL);
  276. type = blob.type.split('/')[1];
  277. if (imageName.split('.').pop() !== type) {
  278. imageName += '.' + type;
  279. }
  280. // Library: FileSaver - saveAs
  281. saveAs(blob, imageName); // eslint-disable-line
  282. } else {
  283. alert('This browser needs a file-server');
  284. w = window.open();
  285. w.document.body.innerHTML = '<img src=' + dataURL + '>';
  286. }
  287. });
  288. // Crop menu action
  289. $btnCrop.on('click', function() {
  290. imageEditor.startDrawingMode('CROPPER');
  291. });
  292. $btnApplyCrop.on('click', function() {
  293. imageEditor.crop(imageEditor.getCropzoneRect()).then(() => {
  294. imageEditor.stopDrawingMode();
  295. $subMenus.removeClass('show');
  296. $hiddenMenus.removeClass('show');
  297. });
  298. });
  299. // Orientation menu action
  300. $btnRotateClockwise.on('click', function() {
  301. imageEditor.rotate(90);
  302. });
  303. $btnRotateCounterClockWise.on('click', function() {
  304. imageEditor.rotate(-90);
  305. });
  306. $btnFlipX.on('click', function() {
  307. imageEditor.flipX();
  308. });
  309. $btnFlipY.on('click', function() {
  310. imageEditor.flipY();
  311. });
  312. // Icon menu action
  313. $btnAddArrowIcon.on('click', function() {
  314. imageEditor.addIcon('arrow');
  315. });
  316. $btnAddCancelIcon.on('click', function() {
  317. imageEditor.addIcon('cancel');
  318. });
  319. $btnAddCustomIcon.on('click', function() {
  320. imageEditor.addIcon('customArrow');
  321. });
  322. iconColorpicker.on('selectColor', function(event) {
  323. imageEditor.changeIconColor(activeObjectId, event.color);
  324. });
  325. // Text menu action
  326. $btnAddText.on('click', function() {
  327. var initText = 'DoubleClick';
  328. imageEditor.startDrawingMode('TEXT');
  329. imageEditor.addText(initText, {
  330. styles: {
  331. fontSize: parseInt($inputTextSizeRange.val(), 10)
  332. }
  333. });
  334. });
  335. $btnChangeTextStyle.on('click', function() {
  336. var styleType = $(this).attr('data-style-type');
  337. var styleObj = {};
  338. var styleObjKey;
  339. switch (styleType) {
  340. case 'bold':
  341. styleObjKey = 'fontWeight';
  342. break;
  343. case 'italic':
  344. styleObjKey = 'fontStyle';
  345. break;
  346. case 'underline':
  347. styleObjKey = 'textDecoration';
  348. break;
  349. case 'left':
  350. styleObjKey = 'textAlign';
  351. break;
  352. case 'center':
  353. styleObjKey = 'textAlign';
  354. break;
  355. case 'right':
  356. styleObjKey = 'textAlign';
  357. break;
  358. default:
  359. styleObjKey = '';
  360. }
  361. styleObj[styleObjKey] = styleType;
  362. imageEditor.changeTextStyle(activeObjectId, styleObj);
  363. });
  364. $inputTextSizeRange.on('change', function() {
  365. imageEditor.changeTextStyle(activeObjectId, {
  366. fontSize: parseInt($(this).val(), 10)
  367. });
  368. });
  369. textColorpicker.on('selectColor', function(event) {
  370. imageEditor.changeTextStyle(activeObjectId, {
  371. fill: event.color
  372. });
  373. });
  374. // Draw line menu action
  375. $btnFreeDrawing.on('click', function() {
  376. var settings = getBrushSettings();
  377. imageEditor.stopDrawingMode();
  378. imageEditor.startDrawingMode('FREE_DRAWING', settings);
  379. });
  380. $btnLineDrawing.on('click', function() {
  381. var settings = getBrushSettings();
  382. imageEditor.stopDrawingMode();
  383. imageEditor.startDrawingMode('LINE_DRAWING', settings);
  384. });
  385. $inputBrushWidthRange.on('change', function() {
  386. imageEditor.setBrush({
  387. width: parseInt($(this).val(), 10)
  388. });
  389. });
  390. brushColorpicker.on('selectColor', function(event) {
  391. imageEditor.setBrush({
  392. color: hexToRGBa(event.color, 0.5)
  393. });
  394. });
  395. // Add shape menu action
  396. $btnAddRect.on('click', function() {
  397. imageEditor.addShape('rect', tui.util.extend({
  398. width: 500,
  399. height: 300
  400. }, shapeOpt));
  401. });
  402. $btnAddSquare.on('click', function() {
  403. imageEditor.addShape('rect', tui.util.extend({
  404. width: 400,
  405. height: 400,
  406. isRegular: true
  407. }, shapeOpt));
  408. });
  409. $btnAddEllipse.on('click', function() {
  410. imageEditor.addShape('circle', tui.util.extend({
  411. rx: 300,
  412. ry: 200
  413. }, shapeOpt));
  414. });
  415. $btnAddCircle.on('click', function() {
  416. imageEditor.addShape('circle', tui.util.extend({
  417. rx: 200,
  418. ry: 200,
  419. isRegular: true
  420. }, shapeOpt));
  421. });
  422. $btnAddTriangle.on('click', function() {
  423. imageEditor.addShape('triangle', tui.util.extend({
  424. width: 500,
  425. height: 400,
  426. isRegular: true
  427. }, shapeOpt));
  428. });
  429. $inputStrokeWidthRange.on('change', function() {
  430. imageEditor.changeShape(activeObjectId, {
  431. strokeWidth: parseInt($(this).val(), 10)
  432. });
  433. });
  434. $inputCheckTransparent.on('change', function() {
  435. var colorType = $('[name="select-color-type"]:checked').val();
  436. var isTransparent = $(this).prop('checked');
  437. var color;
  438. if (!isTransparent) {
  439. color = shapeColorpicker.getColor();
  440. } else {
  441. color = 'transparent';
  442. }
  443. if (colorType === 'stroke') {
  444. imageEditor.changeShape(activeObjectId, {
  445. stroke: color
  446. });
  447. } else if (colorType === 'fill') {
  448. imageEditor.changeShape(activeObjectId, {
  449. fill: color
  450. });
  451. }
  452. });
  453. shapeColorpicker.on('selectColor', function(event) {
  454. var colorType = $('[name="select-color-type"]:checked').val();
  455. var isTransparent = $inputCheckTransparent.prop('checked');
  456. var color = event.color;
  457. if (isTransparent) {
  458. return;
  459. }
  460. if (colorType === 'stroke') {
  461. imageEditor.changeShape(activeObjectId, {
  462. stroke: color
  463. });
  464. } else if (colorType === 'fill') {
  465. imageEditor.changeShape(activeObjectId, {
  466. fill: color
  467. });
  468. }
  469. });
  470. // Load sample image
  471. imageEditor.loadImageFromURL('img/sampleImage.jpg', 'SampleImage').then(() => {
  472. imageEditor.clearUndoStack();
  473. });