MinsoftK

redownload

Showing 276 changed files with 0 additions and 19259 deletions
1 -/* ICON BUTTON */
2 -.tie-icon-add-button
3 - &.icon-bubble .{prefix}-button[data-icontype="icon-bubble"] svg > use.active,
4 - &.icon-heart .{prefix}-button[data-icontype="icon-heart"] svg > use.active,
5 - &.icon-location .{prefix}-button[data-icontype="icon-location"] svg > use.active,
6 - &.icon-polygon .{prefix}-button[data-icontype="icon-polygon"] svg > use.active,
7 - &.icon-star .{prefix}-button[data-icontype="icon-star"] svg > use.active,
8 - &.icon-star-2 .{prefix}-button[data-icontype="icon-star-2"] svg > use.active,
9 - &.icon-arrow-3 .{prefix}-button[data-icontype="icon-arrow-3"] svg > use.active,
10 - &.icon-arrow-2 .{prefix}-button[data-icontype="icon-arrow-2"] svg > use.active,
11 - &.icon-arrow .{prefix}-button[data-icontype="icon-arrow"] svg > use.active,
12 - &.icon-bubble .{prefix}-button[data-icontype="icon-bubble"] svg > use.active
13 - display: block;
14 -
15 -/* DRAW BUTTON */
16 -.tie-draw-line-select-button
17 - &.line .{prefix}-button.line svg > use.normal,
18 - &.free .{prefix}-button.free svg > use.normal
19 - display: none;
20 -
21 - &.line .{prefix}-button.line svg > use.active,
22 - &.free .{prefix}-button.free svg > use.active
23 - display: block;
24 -
25 -/* FLIP BUTTON */
26 -.tie-flip-button
27 - &.resetFlip .{prefix}-button.resetFlip,
28 - &.flipX .{prefix}-button.flipX,
29 - &.flipY .{prefix}-button.flipY
30 - svg > use.normal
31 - display: none;
32 - svg > use.active
33 - display: block;
34 -
35 -/* MASK BUTTON */
36 -.tie-mask-apply.apply.active .{prefix}-button.apply
37 - label
38 - color: #fff;
39 - svg > use.active
40 - display: block;
41 -
42 -/* CROP BUTTON */
43 -.tie-crop-button,
44 -.tie-crop-preset-button
45 - .{prefix}-button.apply
46 - margin-right: 24px;
47 - .{prefix}-button.preset.active svg > use.active
48 - display: block;
49 - .{prefix}-button.apply.active svg > use.active
50 - display: block;
51 -
52 -
53 -/* SHAPE BUTTON */
54 -.tie-shape-button
55 - &.rect .{prefix}-button.rect,
56 - &.circle .{prefix}-button.circle,
57 - &.triangle .{prefix}-button.triangle
58 - svg > use.normal
59 - display: none;
60 - svg > use.active
61 - display: block;
62 -
63 -/* TEXT BUTTON */
64 -.tie-text-effect-button
65 - .{prefix}-button.active svg > use.active
66 - display: block;
67 -.tie-text-align-button
68 - &.left .{prefix}-button.left svg > use.active,
69 - &.center .{prefix}-button.center svg > use.active,
70 - &.right .{prefix}-button.right svg > use.active
71 - display: block;
72 -.tie-mask-image-file,
73 -.tie-icon-image-file
74 - opacity: 0;
75 - position: absolute;
76 - width: 100%;
77 - height: 100%;
78 - border: 1px solid green;
79 - cursor: inherit;
80 - left: 0;
81 - top: 0;
1 -/* VIRTUAL CHECKBOX */
2 -.{prefix}-container
3 - .filter-color-item
4 - display: inline-block;
5 - .tui-image-editor-checkbox
6 - display: block;
7 - .{prefix}-checkbox-wrap
8 - display: inline-block !important;
9 - text-align: left;
10 - .{prefix}-checkbox-wrap.fixed-width
11 - width: 187px;
12 - white-space: normal;
13 - .{prefix}-checkbox
14 - display: inline-block;
15 - margin: 1px 0 1px 0;
16 - input
17 - width: 14px;
18 - height: 14px;
19 - opacity: 0;
20 - > label > span
21 - color: #fff;
22 - height: 14px;
23 - position: relative;
24 - input + label:before,
25 - > label > span:before
26 - content: '';
27 - position: absolute;
28 - width: 14px;
29 - height: 14px;
30 - background-color: #fff;
31 - top: 6px;
32 - left: -19px;
33 - display: inline-block;
34 - margin: 0;
35 - text-align: center;
36 - font-size: 11px;
37 - border: 0;
38 - border-radius: 2px;
39 - padding-top: 1px;
40 - box-sizing: border-box;
41 - input[type='checkbox']:checked + span:before
42 - background-size: cover;
43 - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAMBJREFUKBWVkjEOwjAMRe2WgZW7IIHEDdhghhuwcQ42rlJugAQS54Cxa5cq1QM5TUpByZfS2j9+dlJVt/tX5ZxbS4ZU9VLkQvSHKTIGRaVJYFmKrBbTCJxE2UgCdDzMZDkHrOV6b95V0US6UmgKodujEZbJg0B0ZgEModO5lrY1TMQf1TpyJGBEjD+E2NPN7ukIUDiF/BfEXgRiGEw8NgkffYGYwCi808fpn/6OvfUfsDr/Vc1IfRf8sKnFVqeiVQfDu0tf/nWH9gAAAABJRU5ErkJggg==');
44 -
45 - .{prefix}-selectlist-wrap
46 - position: relative;
47 - select
48 - width: 100%;
49 - height: 28px;
50 - margin-top: 4px;
51 - border: 0;
52 - outline: 0;
53 - border-radius: 0;
54 - border: 1px solid #cbdbdb;
55 - background-color: #fff;
56 - -webkit-appearance: none;
57 - -moz-appearance: none;
58 - appearance: none;
59 - padding: 0 7px 0 10px;
60 - .{prefix}-selectlist
61 - display: none;
62 - position: relative;
63 - top: -1px;
64 - border: 1px solid #ccc;
65 - background-color: #fff;
66 - border-top: 0px;
67 - padding: 4px 0;
68 - li
69 - display: block;
70 - text-align: left;
71 - padding: 7px 10px;
72 - font-family: 'Noto Sans', sans-serif;
73 - li:hover
74 - background-color: rgba(81, 92, 230, 0.05);
75 - .{prefix}-selectlist-wrap:before
76 - content: '';
77 - position: absolute;
78 - display: inline-block;
79 - width: 14px;
80 - height: 14px;
81 - right: 5px;
82 - top: 10px;
83 - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHlJREFUKBVjYBgFOEOAEVkmPDxc89+/f6eAYjzI4kD2FyYmJrOVK1deh4kzwRggGiQBVJCELAZig8SQNYHEmEEEMrh69eo1HR0dfqCYJUickZGxf9WqVf3IakBsFBthklpaWmVA9mEQhrJhUoTp0NBQCRAmrHL4qgAAuu4cWZOZIGsAAAAASUVORK5CYII=');
84 - background-size: cover;
85 - .{prefix}-selectlist-wrap select::-ms-expand
86 - display:none;
1 -/* COLOR PICKER */
2 -.{prefix}-container
3 - div.tui-colorpicker-clearfix
4 - width: 159px;
5 - height: 28px;
6 - border: 1px solid #d5d5d5;
7 - border-radius: 2px;
8 - background-color: #f5f5f5;
9 - margin-top: 6px;
10 - padding: 4px 7px 4px 7px;
11 - .tui-colorpicker-palette-hex
12 - width: 114px;
13 - background-color: #f5f5f5;
14 - border: 0;
15 - font-size: 11px;
16 - margin-top: 2px;
17 - font-family: 'Noto Sans', sans-serif;
18 - .tui-colorpicker-palette-hex[value='#ffffff'] + .tui-colorpicker-palette-preview,
19 - .tui-colorpicker-palette-hex[value=''] + .tui-colorpicker-palette-preview
20 - border: 1px solid #ccc;
21 - .tui-colorpicker-palette-hex[value=''] + .tui-colorpicker-palette-preview
22 - background-size: cover;
23 - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAdBJREFUWAnFl0FuwjAQRZ0ukiugHqFSOQNdseuKW3ALzkA4BateICvUGyCxrtRFd4WuunH/TzykaYJrnLEYaTJJsP2+x8GZZCbQrLU5mj7Bn+EP8HvnCObd+R7xBV5lWfaNON4AnsA38E94qLEt+0yiFaBzAV/Bv+Cxxr4co7hKCDpw1q9wLeNYYdlAwyn8TYt8Hme3+8D5ozcTaMCZ68PXa2tnM2sbEcOZAJhrrpl2DAcTOGNjZPSfCdzkw6JrfbiMv+osBe4y9WOedhm4jZfhbENWuxS44H9Wz/xw4WzqLOAqh1+zycgAwzEMzr5k5gaHOa9ULBwuuDkFlHI1Kl4PJ66kgIpnoywOTmRFAYcbwYk9UMApWkD8zAV5ihcwHk4Rx7gl0IFTQL0EFc+CTQ9OZHWH3YhlVJiVpTHbrTGLhTHLZVgff6s9lyBsI9KduSS83oj+34rTwJutmBmCnMsvozRwZqB5GTkBw6/jdPDu69iJ6BYk6eCcfbcgcQIK/MByaaiMqm8rHcjol2TnpWDhyAKSGdA3FrxtJUToX0ODqatetfGE+8tyEUOV8GY5dGRwLP/MBS4RHQr4bT7NRAQjlcOTfZxmv2G+c4hI8nn+Ax5PG/zhI393AAAAAElFTkSuQmCC');
24 - .tui-colorpicker-palette-preview
25 - border-radius: 100%;
26 - float: left;
27 - width: 17px;
28 - height: 17px;
29 - border: 0;
30 - .color-picker-control
31 - position: absolute;
32 - display: none;
33 - z-index: 99;
34 - width: 192px;
35 - background-color: #fff;
36 - box-shadow: 0 3px 22px 6px rgba(0, 0, 0, .15);
37 - padding: 16px;
38 - border-radius: 2px;
39 - .tui-colorpicker-palette-toggle-slider
40 - display: none;
41 - .tui-colorpicker-palette-button
42 - border: 0;
43 - border-radius: 100%;
44 - margin: 2px;
45 - background-size: cover;
46 - font-size: 1px;
47 - &[title='#ffffff']
48 - border: 1px solid #ccc;
49 - &[title='']
50 - border: 1px solid #ccc;
51 - .triangle
52 - width: 0;
53 - height: 0;
54 - border-right: 7px solid transparent;
55 - border-top: 8px solid #fff;
56 - border-left: 7px solid transparent;
57 - position: absolute;
58 - bottom: -8px;
59 - left: 84px;
60 - .tui-colorpicker-container,
61 - .tui-colorpicker-palette-container ul,
62 - .tui-colorpicker-palette-container
63 - width: 100%;
64 - height: auto;
65 -
66 -
67 - .filter-color-item
68 - .color-picker-control label
69 - font-color: #333;
70 - font-weight: normal;
71 - margin-right: 7pxleft
72 - .tui-image-editor-checkbox
73 - margin-top: 0;
74 - input + label:before,
75 - > label:before
76 - left: -16px;
77 - .color-picker
78 - width: 100%;
79 - height: auto;
80 - .color-picker-value
81 - width: 32px;
82 - height: 32px;
83 - border: 0px;
84 - border-radius: 100%;
85 - margin: auto;
86 - margin-bottom: 1px;
87 - &.transparent
88 - border: 1px solid #cbcbcb;
89 - background-size: cover;
90 - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAdBJREFUWAnFl0FuwjAQRZ0ukiugHqFSOQNdseuKW3ALzkA4BateICvUGyCxrtRFd4WuunH/TzykaYJrnLEYaTJJsP2+x8GZZCbQrLU5mj7Bn+EP8HvnCObd+R7xBV5lWfaNON4AnsA38E94qLEt+0yiFaBzAV/Bv+Cxxr4co7hKCDpw1q9wLeNYYdlAwyn8TYt8Hme3+8D5ozcTaMCZ68PXa2tnM2sbEcOZAJhrrpl2DAcTOGNjZPSfCdzkw6JrfbiMv+osBe4y9WOedhm4jZfhbENWuxS44H9Wz/xw4WzqLOAqh1+zycgAwzEMzr5k5gaHOa9ULBwuuDkFlHI1Kl4PJ66kgIpnoywOTmRFAYcbwYk9UMApWkD8zAV5ihcwHk4Rx7gl0IFTQL0EFc+CTQ9OZHWH3YhlVJiVpTHbrTGLhTHLZVgff6s9lyBsI9KduSS83oj+34rTwJutmBmCnMsvozRwZqB5GTkBw6/jdPDu69iJ6BYk6eCcfbcgcQIK/MByaaiMqm8rHcjol2TnpWDhyAKSGdA3FrxtJUToX0ODqatetfGE+8tyEUOV8GY5dGRwLP/MBS4RHQr4bT7NRAQjlcOTfZxmv2G+c4hI8nn+Ax5PG/zhI393AAAAAElFTkSuQmCC');
91 -
92 - .color-picker-value + label
93 - color: #fff;
94 -
95 - .{prefix}-submenu svg > use
96 - display: none;
97 - .{prefix}-submenu svg > use.normal
98 - display: block;
1 -/* GRID VISUAL OF FLIP AND ROTATE MENU */
2 -.{prefix}-container
3 - .{prefix}-grid-visual
4 - display: none;
5 - position: absolute;
6 - width: 100%;
7 - height: 100%;
8 - border: 1px solid rgba(255,255,255,0.7);
9 - .{prefix}-main.{prefix}-menu-flip,
10 - .{prefix}-main.{prefix}-menu-rotate
11 - .tui-image-editor
12 - transition: none;
13 - .{prefix}-main.{prefix}-menu-flip .{prefix}-grid-visual,
14 - .{prefix}-main.{prefix}-menu-rotate .{prefix}-grid-visual
15 - display: block;
16 - .{prefix}-grid-visual
17 - table
18 - width: 100%;
19 - height: 100%;
20 - border-collapse: collapse;
21 - td
22 - border: 1px solid rgba(255,255,255,0.3);
23 - td.dot:before
24 - content: '';
25 - position: absolute;
26 - box-sizing: border-box;
27 - width: 10px;
28 - height: 10px;
29 - border: 0;
30 - box-shadow: 0 0 1px 0 rgba(0,0,0,0.3);
31 - border-radius: 100%;
32 - background-color: #fff;
33 - td.dot.left-top:before
34 - top: -5px;
35 - left: -5px;
36 - td.dot.right-top:before
37 - top: -5px;
38 - right: -5px;
39 - td.dot.left-bottom:before
40 - bottom: -5px;
41 - left: -5px;
42 - td.dot.right-bottom:before
43 - bottom: -5px;
44 - right: -5px;
1 -/* ICON */
2 -.{prefix}-container
3 - .tie-icon-add-button .{prefix}-button
4 - min-width: 42px;
5 - .svg_ic-menu
6 - .svg_ic-helpmenu
7 - width: 24px;
8 - height: 24px;
9 - .svg_ic-submenu
10 - width: 32px;
11 - height: 32px;
12 - .svg_img-bi
13 - width: 257px;
14 - height: 26px;
15 -
16 - .{prefix}-controls
17 - svg > use
18 - display: none;
19 - .enabled svg:hover > use.hover
20 - .normal svg:hover > use.hover
21 - display: block;
22 - .active svg:hover > use.hover
23 - display: none;
24 - svg > use.normal
25 - display: block;
26 - .active svg > use.active
27 - display: block;
28 - .enabled svg > use.enabled
29 - display: block;
30 - .active svg > use.normal,
31 - .enabled svg > use.normal
32 - display: none;
33 - .help svg > use.disabled,
34 - .help.enabled svg > use.normal
35 - display: block;
36 - .help.enabled svg > use.disabled
37 - display: none;
38 -
39 - .{prefix}-controls:hover
40 - z-index: 3;
1 -prefix = 'tui-image-editor'
2 -
3 -@import 'main.styl'
4 -@import 'gridtable.styl'
5 -@import 'submenu.styl'
6 -@import 'checkbox.styl'
7 -@import 'range.styl'
8 -@import 'position.styl'
9 -@import 'icon.styl'
10 -@import 'colorpicker.styl'
11 -@import 'buttons.styl'
12 -.{prefix}-container.top
13 - &.{prefix}-top-optimization
14 - .{prefix}-controls ul
15 - text-align: right;
16 - .{prefix}-controls-logo
17 - display: none;
1 -body > textarea
2 - position: fixed !important;
3 -
4 -+prefix-classes(prefix)
5 - .-container
6 - margin: 0;
7 - padding: 0;
8 - box-sizing: border-box;
9 - min-height: 300px;
10 - height: 100%;
11 - position: relative;
12 - background-color: #282828;
13 - overflow: hidden;
14 - letter-spacing: 0.3px;
15 -
16 - div, ul, label, input, li
17 - box-sizing: border-box;
18 - margin: 0;
19 - padding: 0;
20 - -ms-user-select: none;
21 - -moz-user-select: -moz-none;
22 - -khtml-user-select: none;
23 - -webkit-user-select: none;
24 - user-select: none;
25 -
26 - .-header
27 - /* BUTTON AND LOGO */
28 - min-width: 533px;
29 - position: absolute;
30 - background-color: #151515;
31 - top: 0;
32 - width: 100%;
33 - .-header-buttons,
34 - .-controls-buttons
35 - float: right;
36 - margin: 8px;
37 -
38 - .-header-logo,
39 - .-controls-logo
40 - float: left;
41 - width: 30%;
42 - padding: 17px;
43 -
44 - .-controls-logo,
45 - .-controls-buttons
46 - width: 270px;
47 - height: 100%;
48 - display: none;
49 -
50 - .-header-buttons button,
51 - .-header-buttons div,
52 - .-controls-buttons button,
53 - .-controls-buttons div
54 - display: inline-block;
55 - position: relative;
56 - width: 120px;
57 - height: 40px;
58 - padding: 0;
59 - line-height: 40px;
60 - outline: none;
61 - border-radius: 20px;
62 - border: 1px solid #ddd;
63 - font-family: 'Noto Sans', sans-serif;
64 - font-size: 12px;
65 - font-weight: bold;
66 - cursor: pointer;
67 - vertical-align: middle;
68 - letter-spacing: 0.3px;
69 - text-align: center;
70 -
71 - .-download-btn
72 - background-color: #fdba3b;
73 - border-color: #fdba3b;
74 - color: #fff;
75 - .-load-btn
76 - position: absolute;
77 - left: 0;
78 - right: 0;
79 - display: inline-block;
80 - top: 0;
81 - bottom: 0;
82 - width: 100%;
83 - cursor: pointer;
84 - opacity: 0;
85 - .-main-container
86 - position: absolute;
87 - width: 100%;
88 - top: 0;
89 - bottom: 64px;
90 - .-main
91 - position: absolute;
92 - text-align: center;
93 - top: 64px;
94 - bottom: 0;
95 - right: 0;
96 - left: 0;
97 - .-wrap
98 - position: absolute;
99 - bottom: 0;
100 - width: 100%;
101 - overflow: auto;
102 - .-size-wrap
103 - display: table;
104 - width: 100%;
105 - height: 100%
106 - .-align-wrap
107 - display: table-cell;
108 - vertical-align: middle;
109 - .
110 - position: relative;
111 - display: inline-block;
112 -
113 -
114 -/* BIG MENU */
115 -.{prefix}-container
116 - .{prefix}-menu
117 - width: auto;
118 - list-style: none;
119 - padding: 0;
120 - margin: 0 auto;
121 - display: table-cell;
122 - text-align: center;
123 - vertical-align: middle;
124 - white-space: nowrap;
125 - > .{prefix}-item
126 - position: relative;
127 - display: inline-block;
128 - border-radius: 2px;
129 - padding: 7px 8px 3px 8px;
130 - cursor: pointer;
131 - margin: 0 4px;
132 - > .{prefix}-item[tooltip-content]:hover
133 - &:before
134 - content: '';
135 - position: absolute;
136 - display: inline-block;
137 - margin: 0 auto 0;
138 - width: 0;
139 - height: 0;
140 - border-right: 7px solid transparent;
141 - border-top: 7px solid #2f2f2f;
142 - border-left: 7px solid transparent;
143 - left: 13px;
144 - top: -2px;
145 - &:after
146 - content: attr(tooltip-content);
147 - position: absolute;
148 - display: inline-block;
149 - background-color: #2f2f2f;
150 - color: #fff;
151 - padding: 5px 8px;
152 - font-size: 11px;
153 - font-weight: lighter;
154 - border-radius: 3px;
155 - max-height: 23px;
156 - top: -25px;
157 - left: 0;
158 - min-width: 24px;
159 - > .{prefix}-item.active
160 - background-color: #fff;
161 - transition: all .3s ease;
162 - .{prefix}-wrap
163 - position: absolute;
1 -/* POSITION LEFT */
2 -.{prefix}-container
3 - &.left
4 - .{prefix}-menu
5 - > .{prefix}-item[tooltip-content]
6 - &:before
7 - left: 28px;
8 - top: 11px;
9 - border-right: 7px solid #2f2f2f;
10 - border-top: 7px solid transparent;
11 - border-bottom: 7px solid transparent;
12 - &:after
13 - top: 7px;
14 - left: 42px;
15 - white-space: nowrap;
16 - .{prefix}-submenu
17 - left: 0;
18 - height: 100%;
19 - width: 248px;
20 - .{prefix}-main-container
21 - left: 64px;
22 - width: calc(100% - 64px);
23 - height: 100%;
24 - .{prefix}-controls
25 - width: 64px;
26 - height: 100%;
27 - display: table;
28 -
29 -/* POSITION LEFT & RIGHT */
30 -.{prefix}-container
31 - &.left, &.right
32 - .{prefix}-menu
33 - white-space: inherit;
34 - .{prefix}-submenu
35 - white-space: normal;
36 - > div
37 - vertical-align: middle;
38 - .{prefix}-controls li
39 - display: inline-block;
40 - margin: 4px auto;
41 - .{prefix}-icpartition
42 - position: relative;
43 - top: -7px;
44 - width: 24px;
45 - height: 1px;
46 - .{prefix}-submenu
47 - .{prefix}-partition
48 - display: block;
49 - width: 75%;
50 - margin: auto;
51 - > div
52 - border-left: 0;
53 - height:10px;
54 - border-bottom: 1px solid #3c3c3c;
55 - width: 100%;
56 - margin: 0;
57 - .{prefix}-submenu-align
58 - margin-right: 0;
59 - .{prefix}-submenu-item
60 - li
61 - margin-top: 15px;
62 - .tui-colorpicker-clearfix li
63 - margin-top: 0;
64 -
65 - .{prefix}-checkbox-wrap.fixed-width
66 - width: 182px;
67 - white-space: normal;
68 - .{prefix}-range-wrap.{prefix}-newline label.range
69 - display: block;
70 - text-align: left;
71 - width: 75%;
72 - margin: auto;
73 - .{prefix}-range
74 - width: 136px;
75 -
76 -
77 -/* POSITION RIGIHT */
78 -.{prefix}-container
79 - &.right
80 - .{prefix}-menu
81 - > .{prefix}-item[tooltip-content]
82 - &:before
83 - left: -3px;
84 - top: 11px;
85 - border-left: 7px solid #2f2f2f;
86 - border-top: 7px solid transparent;
87 - border-bottom: 7px solid transparent;
88 - &:after
89 - top: 7px;
90 - left: unset;
91 - right: 43px;
92 - white-space: nowrap;
93 - .{prefix}-submenu
94 - right: 0;
95 - height: 100%;
96 - width: 248px;
97 - .{prefix}-main-container
98 - right: 64px;
99 - width: calc(100% - 64px);
100 - height: 100%;
101 - .{prefix}-controls
102 - right: 0;
103 - width: 64px;
104 - height: 100%;
105 - display: table;
106 -
107 -
108 -/* POSITION TOP & BOTTOM */
109 -.{prefix}-container
110 - &.top, &.bottom
111 - .{prefix}-submenu
112 - .{prefix}-partition.only-left-right
113 - display: none;
114 -
115 -
116 -/* POSITION BOTTOM */
117 -.{prefix}-container
118 - &.bottom .tui-image-editor-submenu > div
119 - padding-bottom: 24px;
120 -
121 -/* POSITION TOP */
122 -.{prefix}-container
123 - &.top
124 - .color-picker-control .triangle
125 - top: -8px;
126 - border-right: 7px solid transparent;
127 - border-top: 0px;
128 - border-left: 7px solid transparent;
129 - border-bottom: 8px solid #fff;
130 - .{prefix}-size-wrap
131 - height: 100%;
132 - .{prefix}-main-container
133 - bottom: 0;
134 - .{prefix}-menu
135 - > .{prefix}-item[tooltip-content]
136 - &:before
137 - left: 13px;
138 - border-top: 0;
139 - border-bottom: 7px solid #2f2f2f;
140 - top: 33px;
141 - &:after
142 - top: 38px;
143 - .{prefix}-submenu
144 - top: 0;
145 - bottom: auto;
146 - > div
147 - padding-top: 24px;
148 - vertical-align: top;
149 - .{prefix}-controls-logo
150 - display: table-cell;
151 - .{prefix}-controls-buttons
152 - display: table-cell;
153 - .{prefix}-main
154 - top: 64px;
155 - height: calc(100% - 64px);
156 - .{prefix}-controls
157 - top: 0;
158 - bottom: inherit;
159 -
1 -/* VIRTUAL RANGE */
2 -.{prefix}-container
3 -
4 - .{prefix}-virtual-range-bar
5 - .{prefix}-virtual-range-subbar
6 - .{prefix}-virtual-range-pointer
7 - .{prefix}-disabled
8 - backbround-color: red;
9 -
10 - .{prefix}-range
11 - position: relative;
12 - top: 5px;
13 - width: 166px;
14 - height: 17px;
15 - display: inline-block;
16 - .{prefix}-virtual-range-bar
17 - top: 7px;
18 - position: absolute;
19 - width: 100%;
20 - height: 2px;
21 - background-color: #666666;
22 - .{prefix}-virtual-range-subbar
23 - position: absolute;
24 - height: 100%;
25 - left: 0;
26 - right: 0;
27 - background-color: #d1d1d1;
28 - .{prefix}-virtual-range-pointer
29 - position: absolute;
30 - cursor: pointer;
31 - top: -5px;
32 - left: 0;
33 - width: 12px;
34 - height: 12px;
35 - background-color: #fff;
36 - border-radius: 100%;
37 - .{prefix}-range-wrap
38 - display: inline-block;
39 - margin-left: 4px;
40 - &.short .tui-image-editor-range
41 - width: 100px;
42 - .color-picker-control
43 - .{prefix}-range
44 - width: 108px;
45 - margin-left: 10px;
46 - .{prefix}-virtual-range-pointer
47 - background-color: #333;
48 - .{prefix}-virtual-range-bar
49 - background-color: #ccc;
50 - .{prefix}-virtual-range-subbar
51 - background-color: #606060;
52 - .{prefix}-range-wrap.{prefix}-newline.short
53 - margin-top: -2px;
54 - margin-left: 19px;
55 - label
56 - color: #8e8e8e;
57 - font-weight: normal;
58 - .{prefix}-range-wrap label
59 - vertical-align: baseline;
60 - font-size: 11px;
61 - margin-right: 7px;
62 - color: #fff;
63 - .{prefix}-range-value
64 - cursor: default;
65 - width: 40px;
66 - height: 24px;
67 - outline: none;
68 - border-radius: 2px;
69 - box-shadow: none;
70 - border: 1px solid #d5d5d5;
71 - text-align: center;
72 - background-color: #1c1c1c;
73 - color: #fff;
74 - font-weight: lighter;
75 - vertical-align: baseline;
76 - font-family: 'Noto Sans', sans-serif;
77 - margin-top: 21px;
78 - margin-left: 4px;
79 - .{prefix}-controls
80 - position: absolute;
81 - background-color: #151515;
82 - width: 100%;
83 - height: 64px;
84 - display: table;
85 - bottom: 0;
86 - z-index: 2;
87 - .{prefix}-icpartition
88 - display: inline-block;
89 - background-color: #282828;
90 - width: 1px;
91 - height: 24px;
...\ No newline at end of file ...\ No newline at end of file
1 -/* SUBMENU */
2 -.{prefix}-container
3 - .{prefix}-submenu
4 - display: none;
5 - position: absolute;
6 - bottom: 0;
7 - width:100%;
8 - height: 150px;
9 - white-space: nowrap;
10 - z-index: 2;
11 - .{prefix}-button:hover svg > use.active
12 - display: block;
13 - .{prefix}-submenu-item
14 - li
15 - display: inline-block;
16 - vertical-align: top;
17 - .{prefix}-newline
18 - display: block;
19 - margin-top: 0;
20 - .{prefix}-button
21 - position: relative;
22 - cursor: pointer;
23 - display: inline-block;
24 - font-weight: normal;
25 - font-size: 11px;
26 - margin: 0 9px 0 9px;
27 - .{prefix}-button.preset
28 - margin: 0 9px 20px 5px;
29 - label > span
30 - display: inline-block;
31 - cursor: pointer;
32 - padding-top: 5px;
33 - font-family: "Noto Sans", sans-serif;
34 - font-size: 11px;
35 - .{prefix}-button.apply label,
36 - .{prefix}-button.cancel label
37 - vertical-align: 7px;
38 - > div
39 - display: none;
40 - vertical-align: bottom;
41 - .{prefix}-submenu-style
42 - opacity: 0.95;
43 - z-index: -1;
44 - position: absolute;
45 - top: 0;
46 - bottom: 0;
47 - left: 0;
48 - right: 0;
49 - display: block;
50 -
51 - .{prefix}-partition > div
52 - width: 1px;
53 - height: 52px;
54 - border-left: 1px solid #3c3c3c;
55 - margin: 0 8px 0 8px;
56 - .{prefix}-main.{prefix}-menu-filter .{prefix}-partition > div
57 - height: 108px;
58 - margin: 0 29px 0 0px;
59 - .{prefix}-submenu-align
60 - text-align: left;
61 - margin-right: 30px;
62 - label > span
63 - width: 55px;
64 - white-space: nowrap;
65 - .{prefix}-submenu-align:first-child
66 - margin-right: 0;
67 - label > span
68 - width: 70px;
69 - .{prefix}-main.{prefix}-menu-crop .{prefix}-submenu > div.{prefix}-menu-crop,
70 - .{prefix}-main.{prefix}-menu-flip .{prefix}-submenu > div.{prefix}-menu-flip,
71 - .{prefix}-main.{prefix}-menu-rotate .{prefix}-submenu > div.{prefix}-menu-rotate,
72 - .{prefix}-main.{prefix}-menu-shape .{prefix}-submenu > div.{prefix}-menu-shape,
73 - .{prefix}-main.{prefix}-menu-text .{prefix}-submenu > div.{prefix}-menu-text,
74 - .{prefix}-main.{prefix}-menu-mask .{prefix}-submenu > div.{prefix}-menu-mask,
75 - .{prefix}-main.{prefix}-menu-icon .{prefix}-submenu > div.{prefix}-menu-icon,
76 - .{prefix}-main.{prefix}-menu-draw .{prefix}-submenu > div.{prefix}-menu-draw,
77 - .{prefix}-main.{prefix}-menu-filter .{prefix}-submenu > div.{prefix}-menu-filter
78 - display: table-cell;
79 - .{prefix}-main.{prefix}-menu-crop,
80 - .{prefix}-main.{prefix}-menu-flip,
81 - .{prefix}-main.{prefix}-menu-rotate,
82 - .{prefix}-main.{prefix}-menu-shape,
83 - .{prefix}-main.{prefix}-menu-text,
84 - .{prefix}-main.{prefix}-menu-mask,
85 - .{prefix}-main.{prefix}-menu-icon,
86 - .{prefix}-main.{prefix}-menu-draw,
87 - .{prefix}-main.{prefix}-menu-filter
88 - .{prefix}-submenu
89 - display: table;
90 -
1 -import './js/polyfill';
2 -import ImageEditor from './js/imageEditor';
3 -import './css/index.styl';
4 -
5 -// commands
6 -import './js/command/addIcon';
7 -import './js/command/addImageObject';
8 -import './js/command/addObject';
9 -import './js/command/addShape';
10 -import './js/command/addText';
11 -import './js/command/applyFilter';
12 -import './js/command/changeIconColor';
13 -import './js/command/changeShape';
14 -import './js/command/changeText';
15 -import './js/command/changeTextStyle';
16 -import './js/command/clearObjects';
17 -import './js/command/flip';
18 -import './js/command/loadImage';
19 -import './js/command/removeFilter';
20 -import './js/command/removeObject';
21 -import './js/command/resizeCanvasDimension';
22 -import './js/command/rotate';
23 -import './js/command/setObjectProperties';
24 -import './js/command/setObjectPosition';
25 -import './js/command/changeSelection';
26 -
27 -module.exports = ImageEditor;
1 -import { extend } from 'tui-code-snippet';
2 -import { isSupportFileApi, base64ToBlob, toInteger } from './util';
3 -import Imagetracer from './helper/imagetracer';
4 -
5 -export default {
6 - /**
7 - * Get ui actions
8 - * @returns {Object} actions for ui
9 - * @private
10 - */
11 - getActions() {
12 - return {
13 - main: this._mainAction(),
14 - shape: this._shapeAction(),
15 - crop: this._cropAction(),
16 - flip: this._flipAction(),
17 - rotate: this._rotateAction(),
18 - text: this._textAction(),
19 - mask: this._maskAction(),
20 - draw: this._drawAction(),
21 - icon: this._iconAction(),
22 - filter: this._filterAction(),
23 - };
24 - },
25 -
26 - /**
27 - * Main Action
28 - * @returns {Object} actions for ui main
29 - * @private
30 - */
31 - _mainAction() {
32 - const exitCropOnAction = () => {
33 - if (this.ui.submenu === 'crop') {
34 - this.stopDrawingMode();
35 - this.ui.changeMenu('crop');
36 - }
37 - };
38 - const setAngleRangeBarOnAction = (angle) => {
39 - if (this.ui.submenu === 'rotate') {
40 - this.ui.rotate.setRangeBarAngle('setAngle', angle);
41 - }
42 - };
43 - const setFilterStateRangeBarOnAction = (filterOptions) => {
44 - if (this.ui.submenu === 'filter') {
45 - this.ui.filter.setFilterState(filterOptions);
46 - }
47 - };
48 - const onEndUndoRedo = (result) => {
49 - setAngleRangeBarOnAction(result);
50 - setFilterStateRangeBarOnAction(result);
51 -
52 - return result;
53 - };
54 -
55 - return extend(
56 - {
57 - initLoadImage: (imagePath, imageName) =>
58 - this.loadImageFromURL(imagePath, imageName).then((sizeValue) => {
59 - exitCropOnAction();
60 - this.ui.initializeImgUrl = imagePath;
61 - this.ui.resizeEditor({ imageSize: sizeValue });
62 - this.clearUndoStack();
63 - }),
64 - undo: () => {
65 - if (!this.isEmptyUndoStack()) {
66 - exitCropOnAction();
67 - this.deactivateAll();
68 - this.undo().then(onEndUndoRedo);
69 - }
70 - },
71 - redo: () => {
72 - if (!this.isEmptyRedoStack()) {
73 - exitCropOnAction();
74 - this.deactivateAll();
75 - this.redo().then(onEndUndoRedo);
76 - }
77 - },
78 - reset: () => {
79 - exitCropOnAction();
80 - this.loadImageFromURL(this.ui.initializeImgUrl, 'resetImage').then((sizeValue) => {
81 - exitCropOnAction();
82 - this.ui.resizeEditor({ imageSize: sizeValue });
83 - this.clearUndoStack();
84 - });
85 - },
86 - delete: () => {
87 - this.ui.changeHelpButtonEnabled('delete', false);
88 - exitCropOnAction();
89 - this.removeActiveObject();
90 - this.activeObjectId = null;
91 - },
92 - deleteAll: () => {
93 - exitCropOnAction();
94 - this.clearObjects();
95 - this.ui.changeHelpButtonEnabled('delete', false);
96 - this.ui.changeHelpButtonEnabled('deleteAll', false);
97 - },
98 - load: (file) => {
99 - if (!isSupportFileApi()) {
100 - alert('This browser does not support file-api');
101 - }
102 -
103 - this.ui.initializeImgUrl = URL.createObjectURL(file);
104 - this.loadImageFromFile(file)
105 - .then((sizeValue) => {
106 - exitCropOnAction();
107 - this.clearUndoStack();
108 - this.ui.activeMenuEvent();
109 - this.ui.resizeEditor({ imageSize: sizeValue });
110 - })
111 - ['catch']((message) => Promise.reject(message));
112 - },
113 - download: () => {
114 - const dataURL = this.toDataURL();
115 - let imageName = this.getImageName();
116 - let blob, type, w;
117 -
118 - if (isSupportFileApi() && window.saveAs) {
119 - blob = base64ToBlob(dataURL);
120 - type = blob.type.split('/')[1];
121 - if (imageName.split('.').pop() !== type) {
122 - imageName += `.${type}`;
123 - }
124 - saveAs(blob, imageName); // eslint-disable-line
125 - } else {
126 - w = window.open();
127 - w.document.body.innerHTML = `<img src='${dataURL}'>`;
128 - }
129 - },
130 - },
131 - this._commonAction()
132 - );
133 - },
134 -
135 - /**
136 - * Icon Action
137 - * @returns {Object} actions for ui icon
138 - * @private
139 - */
140 - _iconAction() {
141 - return extend(
142 - {
143 - changeColor: (color) => {
144 - if (this.activeObjectId) {
145 - this.changeIconColor(this.activeObjectId, color);
146 - }
147 - },
148 - addIcon: (iconType, iconColor) => {
149 - this.startDrawingMode('ICON');
150 - this.setDrawingIcon(iconType, iconColor);
151 - },
152 - cancelAddIcon: () => {
153 - this.ui.icon.clearIconType();
154 - this.changeSelectableAll(true);
155 - this.changeCursor('default');
156 - this.stopDrawingMode();
157 - },
158 - registDefalutIcons: (type, path) => {
159 - const iconObj = {};
160 - iconObj[type] = path;
161 - this.registerIcons(iconObj);
162 - },
163 - registCustomIcon: (imgUrl, file) => {
164 - const imagetracer = new Imagetracer();
165 - imagetracer.imageToSVG(
166 - imgUrl,
167 - (svgstr) => {
168 - const [, svgPath] = svgstr.match(/path[^>]*d="([^"]*)"/);
169 - const iconObj = {};
170 - iconObj[file.name] = svgPath;
171 - this.registerIcons(iconObj);
172 - this.addIcon(file.name, {
173 - left: 100,
174 - top: 100,
175 - });
176 - },
177 - Imagetracer.tracerDefaultOption()
178 - );
179 - },
180 - },
181 - this._commonAction()
182 - );
183 - },
184 -
185 - /**
186 - * Draw Action
187 - * @returns {Object} actions for ui draw
188 - * @private
189 - */
190 - _drawAction() {
191 - return extend(
192 - {
193 - setDrawMode: (type, settings) => {
194 - this.stopDrawingMode();
195 - if (type === 'free') {
196 - this.startDrawingMode('FREE_DRAWING', settings);
197 - } else {
198 - this.startDrawingMode('LINE_DRAWING', settings);
199 - }
200 - },
201 - setColor: (color) => {
202 - this.setBrush({
203 - color,
204 - });
205 - },
206 - },
207 - this._commonAction()
208 - );
209 - },
210 -
211 - /**
212 - * Mask Action
213 - * @returns {Object} actions for ui mask
214 - * @private
215 - */
216 - _maskAction() {
217 - return extend(
218 - {
219 - loadImageFromURL: (imgUrl, file) =>
220 - this.loadImageFromURL(this.toDataURL(), 'FilterImage').then(() => {
221 - this.addImageObject(imgUrl).then(() => {
222 - URL.revokeObjectURL(file);
223 - });
224 - }),
225 - applyFilter: () => {
226 - this.applyFilter('mask', {
227 - maskObjId: this.activeObjectId,
228 - });
229 - },
230 - },
231 - this._commonAction()
232 - );
233 - },
234 -
235 - /**
236 - * Text Action
237 - * @returns {Object} actions for ui text
238 - * @private
239 - */
240 - _textAction() {
241 - return extend(
242 - {
243 - changeTextStyle: (styleObj, isSilent) => {
244 - if (this.activeObjectId) {
245 - this.changeTextStyle(this.activeObjectId, styleObj, isSilent);
246 - }
247 - },
248 - },
249 - this._commonAction()
250 - );
251 - },
252 -
253 - /**
254 - * Rotate Action
255 - * @returns {Object} actions for ui rotate
256 - * @private
257 - */
258 - _rotateAction() {
259 - return extend(
260 - {
261 - rotate: (angle, isSilent) => {
262 - this.rotate(angle, isSilent);
263 - this.ui.resizeEditor();
264 - this.ui.rotate.setRangeBarAngle('rotate', angle);
265 - },
266 - setAngle: (angle, isSilent) => {
267 - this.setAngle(angle, isSilent);
268 - this.ui.resizeEditor();
269 - this.ui.rotate.setRangeBarAngle('setAngle', angle);
270 - },
271 - },
272 - this._commonAction()
273 - );
274 - },
275 -
276 - /**
277 - * Shape Action
278 - * @returns {Object} actions for ui shape
279 - * @private
280 - */
281 - _shapeAction() {
282 - return extend(
283 - {
284 - changeShape: (changeShapeObject, isSilent) => {
285 - if (this.activeObjectId) {
286 - this.changeShape(this.activeObjectId, changeShapeObject, isSilent);
287 - }
288 - },
289 - setDrawingShape: (shapeType) => {
290 - this.setDrawingShape(shapeType);
291 - },
292 - },
293 - this._commonAction()
294 - );
295 - },
296 -
297 - /**
298 - * Crop Action
299 - * @returns {Object} actions for ui crop
300 - * @private
301 - */
302 - _cropAction() {
303 - return extend(
304 - {
305 - crop: () => {
306 - const cropRect = this.getCropzoneRect();
307 - if (cropRect) {
308 - this.crop(cropRect)
309 - .then(() => {
310 - this.stopDrawingMode();
311 - this.ui.resizeEditor();
312 - this.ui.changeMenu('crop');
313 - })
314 - ['catch']((message) => Promise.reject(message));
315 - }
316 - },
317 - cancel: () => {
318 - this.stopDrawingMode();
319 - this.ui.changeMenu('crop');
320 - },
321 - /* eslint-disable */
322 - preset: (presetType) => {
323 - switch (presetType) {
324 - case 'preset-square':
325 - this.setCropzoneRect(1 / 1);
326 - break;
327 - case 'preset-3-2':
328 - this.setCropzoneRect(3 / 2);
329 - break;
330 - case 'preset-4-3':
331 - this.setCropzoneRect(4 / 3);
332 - break;
333 - case 'preset-5-4':
334 - this.setCropzoneRect(5 / 4);
335 - break;
336 - case 'preset-7-5':
337 - this.setCropzoneRect(7 / 5);
338 - break;
339 - case 'preset-16-9':
340 - this.setCropzoneRect(16 / 9);
341 - break;
342 - default:
343 - this.setCropzoneRect();
344 - this.ui.crop.changeApplyButtonStatus(false);
345 - break;
346 - }
347 - },
348 - },
349 - this._commonAction()
350 - );
351 - },
352 -
353 - /**
354 - * Flip Action
355 - * @returns {Object} actions for ui flip
356 - * @private
357 - */
358 - _flipAction() {
359 - return extend(
360 - {
361 - flip: (flipType) => this[flipType](),
362 - },
363 - this._commonAction()
364 - );
365 - },
366 -
367 - /**
368 - * Filter Action
369 - * @returns {Object} actions for ui filter
370 - * @private
371 - */
372 - _filterAction() {
373 - return extend(
374 - {
375 - applyFilter: (applying, type, options, isSilent) => {
376 - if (applying) {
377 - this.applyFilter(type, options, isSilent);
378 - } else if (this.hasFilter(type)) {
379 - this.removeFilter(type);
380 - }
381 - },
382 - },
383 - this._commonAction()
384 - );
385 - },
386 -
387 - /**
388 - * Image Editor Event Observer
389 - */
390 - setReAction() {
391 - this.on({
392 - undoStackChanged: (length) => {
393 - if (length) {
394 - this.ui.changeHelpButtonEnabled('undo', true);
395 - this.ui.changeHelpButtonEnabled('reset', true);
396 - } else {
397 - this.ui.changeHelpButtonEnabled('undo', false);
398 - this.ui.changeHelpButtonEnabled('reset', false);
399 - }
400 - this.ui.resizeEditor();
401 - },
402 - redoStackChanged: (length) => {
403 - if (length) {
404 - this.ui.changeHelpButtonEnabled('redo', true);
405 - } else {
406 - this.ui.changeHelpButtonEnabled('redo', false);
407 - }
408 - this.ui.resizeEditor();
409 - },
410 - /* eslint-disable complexity */
411 - objectActivated: (obj) => {
412 - this.activeObjectId = obj.id;
413 -
414 - this.ui.changeHelpButtonEnabled('delete', true);
415 - this.ui.changeHelpButtonEnabled('deleteAll', true);
416 -
417 - if (obj.type === 'cropzone') {
418 - this.ui.crop.changeApplyButtonStatus(true);
419 - } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) {
420 - this.stopDrawingMode();
421 - if (this.ui.submenu !== 'shape') {
422 - this.ui.changeMenu('shape', false, false);
423 - }
424 - this.ui.shape.setShapeStatus({
425 - strokeColor: obj.stroke,
426 - strokeWidth: obj.strokeWidth,
427 - fillColor: obj.fill,
428 - });
429 -
430 - this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height));
431 - } else if (obj.type === 'path' || obj.type === 'line') {
432 - if (this.ui.submenu !== 'draw') {
433 - this.ui.changeMenu('draw', false, false);
434 - this.ui.draw.changeStandbyMode();
435 - }
436 - } else if (['i-text', 'text'].indexOf(obj.type) > -1) {
437 - if (this.ui.submenu !== 'text') {
438 - this.ui.changeMenu('text', false, false);
439 - }
440 -
441 - this.ui.text.setTextStyleStateOnAction(obj);
442 - } else if (obj.type === 'icon') {
443 - this.stopDrawingMode();
444 - if (this.ui.submenu !== 'icon') {
445 - this.ui.changeMenu('icon', false, false);
446 - }
447 - this.ui.icon.setIconPickerColor(obj.fill);
448 - }
449 - },
450 - /* eslint-enable complexity */
451 - addText: (pos) => {
452 - const { textColor: fill, fontSize, fontStyle, fontWeight, underline } = this.ui.text;
453 - const fontFamily = 'Noto Sans';
454 -
455 - this.addText('Double Click', {
456 - position: pos.originPosition,
457 - styles: { fill, fontSize, fontFamily, fontStyle, fontWeight, underline },
458 - }).then(() => {
459 - this.changeCursor('default');
460 - });
461 - },
462 - addObjectAfter: (obj) => {
463 - if (obj.type === 'icon') {
464 - this.ui.icon.changeStandbyMode();
465 - } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) {
466 - this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height));
467 - this.ui.shape.changeStandbyMode();
468 - }
469 - },
470 - objectScaled: (obj) => {
471 - if (['i-text', 'text'].indexOf(obj.type) > -1) {
472 - this.ui.text.fontSize = toInteger(obj.fontSize);
473 - } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) >= 0) {
474 - const { width, height } = obj;
475 - const strokeValue = this.ui.shape.getStrokeValue();
476 -
477 - if (width < strokeValue) {
478 - this.ui.shape.setStrokeValue(width);
479 - }
480 - if (height < strokeValue) {
481 - this.ui.shape.setStrokeValue(height);
482 - }
483 - }
484 - },
485 - selectionCleared: () => {
486 - this.activeObjectId = null;
487 - if (this.ui.submenu === 'text') {
488 - this.changeCursor('text');
489 - } else if (this.ui.submenu !== 'draw' && this.ui.submenu !== 'crop') {
490 - this.stopDrawingMode();
491 - }
492 - },
493 - });
494 - },
495 -
496 - /**
497 - * Common Action
498 - * @returns {Object} common actions for ui
499 - * @private
500 - */
501 - _commonAction() {
502 - return {
503 - modeChange: (menu) => {
504 - switch (menu) {
505 - case 'text':
506 - this._changeActivateMode('TEXT');
507 - break;
508 - case 'crop':
509 - this.startDrawingMode('CROPPER');
510 - break;
511 - case 'shape':
512 - this._changeActivateMode('SHAPE');
513 - this.setDrawingShape(this.ui.shape.type, this.ui.shape.options);
514 - break;
515 - default:
516 - break;
517 - }
518 - },
519 - deactivateAll: this.deactivateAll.bind(this),
520 - changeSelectableAll: this.changeSelectableAll.bind(this),
521 - discardSelection: this.discardSelection.bind(this),
522 - stopDrawingMode: this.stopDrawingMode.bind(this),
523 - };
524 - },
525 -
526 - /**
527 - * Mixin
528 - * @param {ImageEditor} ImageEditor instance
529 - */
530 - mixin(ImageEditor) {
531 - extend(ImageEditor.prototype, this);
532 - },
533 -};
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add an icon
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { componentNames, commandNames } from '../consts';
8 -
9 -const { ICON } = componentNames;
10 -
11 -const command = {
12 - name: commandNames.ADD_ICON,
13 -
14 - /**
15 - * Add an icon
16 - * @param {Graphics} graphics - Graphics instance
17 - * @param {string} type - Icon type ('arrow', 'cancel', custom icon name)
18 - * @param {Object} options - Icon options
19 - * @param {string} [options.fill] - Icon foreground color
20 - * @param {string} [options.left] - Icon x position
21 - * @param {string} [options.top] - Icon y position
22 - * @returns {Promise}
23 - */
24 - execute(graphics, type, options) {
25 - const iconComp = graphics.getComponent(ICON);
26 -
27 - return iconComp.add(type, options).then((objectProps) => {
28 - this.undoData.object = graphics.getObject(objectProps.id);
29 -
30 - return objectProps;
31 - });
32 - },
33 - /**
34 - * @param {Graphics} graphics - Graphics instance
35 - * @returns {Promise}
36 - */
37 - undo(graphics) {
38 - graphics.remove(this.undoData.object);
39 -
40 - return Promise.resolve();
41 - },
42 -};
43 -
44 -commandFactory.register(command);
45 -
46 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add an image object
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.ADD_IMAGE_OBJECT,
11 -
12 - /**
13 - * Add an image object
14 - * @param {Graphics} graphics - Graphics instance
15 - * @param {string} imgUrl - Image url to make object
16 - * @returns {Promise}
17 - */
18 - execute(graphics, imgUrl) {
19 - return graphics.addImageObject(imgUrl).then((objectProps) => {
20 - this.undoData.object = graphics.getObject(objectProps.id);
21 -
22 - return objectProps;
23 - });
24 - },
25 - /**
26 - * @param {Graphics} graphics - Graphics instance
27 - * @returns {Promise}
28 - */
29 - undo(graphics) {
30 - graphics.remove(this.undoData.object);
31 -
32 - return Promise.resolve();
33 - },
34 -};
35 -
36 -commandFactory.register(command);
37 -
38 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add an object
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames, rejectMessages } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.ADD_OBJECT,
11 -
12 - /**
13 - * Add an object
14 - * @param {Graphics} graphics - Graphics instance
15 - * @param {Object} object - Fabric object
16 - * @returns {Promise}
17 - */
18 - execute(graphics, object) {
19 - return new Promise((resolve, reject) => {
20 - if (!graphics.contains(object)) {
21 - graphics.add(object);
22 - resolve(object);
23 - } else {
24 - reject(rejectMessages.addedObject);
25 - }
26 - });
27 - },
28 - /**
29 - * @param {Graphics} graphics - Graphics instance
30 - * @param {Object} object - Fabric object
31 - * @returns {Promise}
32 - */
33 - undo(graphics, object) {
34 - return new Promise((resolve, reject) => {
35 - if (graphics.contains(object)) {
36 - graphics.remove(object);
37 - resolve(object);
38 - } else {
39 - reject(rejectMessages.noObject);
40 - }
41 - });
42 - },
43 -};
44 -
45 -commandFactory.register(command);
46 -
47 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add a shape
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { componentNames, commandNames } from '../consts';
8 -
9 -const { SHAPE } = componentNames;
10 -
11 -const command = {
12 - name: commandNames.ADD_SHAPE,
13 -
14 - /**
15 - * Add a shape
16 - * @param {Graphics} graphics - Graphics instance
17 - * @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle')
18 - * @param {Object} options - Shape options
19 - * @param {string} [options.fill] - Shape foreground color (ex: '#fff', 'transparent')
20 - * @param {string} [options.stroke] - Shape outline color
21 - * @param {number} [options.strokeWidth] - Shape outline width
22 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
23 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
24 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
25 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
26 - * @param {number} [options.left] - Shape x position
27 - * @param {number} [options.top] - Shape y position
28 - * @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
29 - * @returns {Promise}
30 - */
31 - execute(graphics, type, options) {
32 - const shapeComp = graphics.getComponent(SHAPE);
33 -
34 - return shapeComp.add(type, options).then((objectProps) => {
35 - this.undoData.object = graphics.getObject(objectProps.id);
36 -
37 - return objectProps;
38 - });
39 - },
40 - /**
41 - * @param {Graphics} graphics - Graphics instance
42 - * @returns {Promise}
43 - */
44 - undo(graphics) {
45 - graphics.remove(this.undoData.object);
46 -
47 - return Promise.resolve();
48 - },
49 -};
50 -
51 -commandFactory.register(command);
52 -
53 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add a text object
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { componentNames, commandNames, rejectMessages } from '../consts';
8 -const { TEXT } = componentNames;
9 -
10 -const command = {
11 - name: commandNames.ADD_TEXT,
12 -
13 - /**
14 - * Add a text object
15 - * @param {Graphics} graphics - Graphics instance
16 - * @param {string} text - Initial input text
17 - * @param {Object} [options] Options for text styles
18 - * @param {Object} [options.styles] Initial styles
19 - * @param {string} [options.styles.fill] Color
20 - * @param {string} [options.styles.fontFamily] Font type for text
21 - * @param {number} [options.styles.fontSize] Size
22 - * @param {string} [options.styles.fontStyle] Type of inclination (normal / italic)
23 - * @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold)
24 - * @param {string} [options.styles.textAlign] Type of text align (left / center / right)
25 - * @param {string} [options.styles.textDecoration] Type of line (underline / line-through / overline)
26 - * @param {{x: number, y: number}} [options.position] - Initial position
27 - * @returns {Promise}
28 - */
29 - execute(graphics, text, options) {
30 - const textComp = graphics.getComponent(TEXT);
31 -
32 - if (this.undoData.object) {
33 - const undoObject = this.undoData.object;
34 -
35 - return new Promise((resolve, reject) => {
36 - if (!graphics.contains(undoObject)) {
37 - graphics.add(undoObject);
38 - resolve(undoObject);
39 - } else {
40 - reject(rejectMessages.redo);
41 - }
42 - });
43 - }
44 -
45 - return textComp.add(text, options).then((objectProps) => {
46 - const { id } = objectProps;
47 - const textObject = graphics.getObject(id);
48 -
49 - this.undoData.object = textObject;
50 -
51 - return objectProps;
52 - });
53 - },
54 - /**
55 - * @param {Graphics} graphics - Graphics instance
56 - * @returns {Promise}
57 - */
58 - undo(graphics) {
59 - graphics.remove(this.undoData.object);
60 -
61 - return Promise.resolve();
62 - },
63 -};
64 -
65 -commandFactory.register(command);
66 -
67 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Apply a filter into an image
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import commandFactory from '../factory/command';
7 -import { componentNames, rejectMessages, commandNames } from '../consts';
8 -
9 -const { FILTER } = componentNames;
10 -
11 -/**
12 - * Chched data for undo
13 - * @type {Object}
14 - */
15 -let chchedUndoDataForSilent = null;
16 -
17 -/**
18 - * Make undoData
19 - * @param {string} type - Filter type
20 - * @param {Object} prevfilterOption - prev Filter options
21 - * @param {Object} options - Filter options
22 - * @returns {object} - undo data
23 - */
24 -function makeUndoData(type, prevfilterOption, options) {
25 - const undoData = {};
26 -
27 - if (type === 'mask') {
28 - undoData.object = options.mask;
29 - }
30 -
31 - undoData.options = prevfilterOption;
32 -
33 - return undoData;
34 -}
35 -
36 -const command = {
37 - name: commandNames.APPLY_FILTER,
38 -
39 - /**
40 - * Apply a filter into an image
41 - * @param {Graphics} graphics - Graphics instance
42 - * @param {string} type - Filter type
43 - * @param {Object} options - Filter options
44 - * @param {number} options.maskObjId - masking image object id
45 - * @param {boolean} isSilent - is silent execution or not
46 - * @returns {Promise}
47 - */
48 - execute(graphics, type, options, isSilent) {
49 - const filterComp = graphics.getComponent(FILTER);
50 -
51 - if (type === 'mask') {
52 - const maskObj = graphics.getObject(options.maskObjId);
53 -
54 - if (!(maskObj && maskObj.isType('image'))) {
55 - return Promise.reject(rejectMessages.invalidParameters);
56 - }
57 -
58 - snippet.extend(options, { mask: maskObj });
59 - graphics.remove(options.mask);
60 - }
61 - if (!this.isRedo) {
62 - const prevfilterOption = filterComp.getOptions(type);
63 - const undoData = makeUndoData(type, prevfilterOption, options);
64 -
65 - chchedUndoDataForSilent = this.setUndoData(undoData, chchedUndoDataForSilent, isSilent);
66 - }
67 -
68 - return filterComp.add(type, options);
69 - },
70 - /**
71 - * @param {Graphics} graphics - Graphics instance
72 - * @param {string} type - Filter type
73 - * @returns {Promise}
74 - */
75 - undo(graphics, type) {
76 - const filterComp = graphics.getComponent(FILTER);
77 -
78 - if (type === 'mask') {
79 - const mask = this.undoData.object;
80 - graphics.add(mask);
81 - graphics.setActiveObject(mask);
82 -
83 - return filterComp.remove(type);
84 - }
85 -
86 - // options changed case
87 - if (this.undoData.options) {
88 - return filterComp.add(type, this.undoData.options);
89 - }
90 -
91 - // filter added case
92 - return filterComp.remove(type);
93 - },
94 -};
95 -
96 -commandFactory.register(command);
97 -
98 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Change icon color
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { componentNames, rejectMessages, commandNames } from '../consts';
8 -
9 -const { ICON } = componentNames;
10 -
11 -const command = {
12 - name: commandNames.CHANGE_ICON_COLOR,
13 -
14 - /**
15 - * Change icon color
16 - * @param {Graphics} graphics - Graphics instance
17 - * @param {number} id - object id
18 - * @param {string} color - Color for icon
19 - * @returns {Promise}
20 - */
21 - execute(graphics, id, color) {
22 - return new Promise((resolve, reject) => {
23 - const iconComp = graphics.getComponent(ICON);
24 - const targetObj = graphics.getObject(id);
25 -
26 - if (!targetObj) {
27 - reject(rejectMessages.noObject);
28 - }
29 -
30 - this.undoData.object = targetObj;
31 - this.undoData.color = iconComp.getColor(targetObj);
32 - iconComp.setColor(color, targetObj);
33 - resolve();
34 - });
35 - },
36 - /**
37 - * @param {Graphics} graphics - Graphics instance
38 - * @returns {Promise}
39 - */
40 - undo(graphics) {
41 - const iconComp = graphics.getComponent(ICON);
42 - const { object: icon, color } = this.undoData;
43 -
44 - iconComp.setColor(color, icon);
45 -
46 - return Promise.resolve();
47 - },
48 -};
49 -
50 -commandFactory.register(command);
51 -
52 -export default command;
1 -/**
2 - * @author NHN. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview change selection
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames } from '../consts';
8 -import { getCachedUndoDataForDimension } from '../helper/selectionModifyHelper';
9 -
10 -const command = {
11 - name: commandNames.CHANGE_SELECTION,
12 -
13 - execute(graphics, props) {
14 - if (this.isRedo) {
15 - props.forEach((prop) => {
16 - graphics.setObjectProperties(prop.id, prop);
17 - });
18 - } else {
19 - this.undoData = getCachedUndoDataForDimension();
20 - }
21 -
22 - return Promise.resolve();
23 - },
24 - undo(graphics) {
25 - this.undoData.forEach((datum) => {
26 - graphics.setObjectProperties(datum.id, datum);
27 - });
28 -
29 - return Promise.resolve();
30 - },
31 -};
32 -
33 -commandFactory.register(command);
34 -
35 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview change a shape
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import { Promise } from '../util';
7 -import commandFactory from '../factory/command';
8 -import { componentNames, rejectMessages, commandNames } from '../consts';
9 -
10 -const { SHAPE } = componentNames;
11 -
12 -/**
13 - * Chched data for undo
14 - * @type {Object}
15 - */
16 -let chchedUndoDataForSilent = null;
17 -
18 -/**
19 - * Make undoData
20 - * @param {object} options - shape options
21 - * @param {Component} targetObj - shape component
22 - * @returns {object} - undo data
23 - */
24 -function makeUndoData(options, targetObj) {
25 - const undoData = {
26 - object: targetObj,
27 - options: {},
28 - };
29 -
30 - snippet.forEachOwnProperties(options, (value, key) => {
31 - undoData.options[key] = targetObj[key];
32 - });
33 -
34 - return undoData;
35 -}
36 -
37 -const command = {
38 - name: commandNames.CHANGE_SHAPE,
39 -
40 - /**
41 - * Change a shape
42 - * @param {Graphics} graphics - Graphics instance
43 - * @param {number} id - object id
44 - * @param {Object} options - Shape options
45 - * @param {string} [options.fill] - Shape foreground color (ex: '#fff', 'transparent')
46 - * @param {string} [options.stroke] - Shape outline color
47 - * @param {number} [options.strokeWidth] - Shape outline width
48 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
49 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
50 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
51 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
52 - * @param {number} [options.left] - Shape x position
53 - * @param {number} [options.top] - Shape y position
54 - * @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
55 - * @param {boolean} isSilent - is silent execution or not
56 - * @returns {Promise}
57 - */
58 - execute(graphics, id, options, isSilent) {
59 - const shapeComp = graphics.getComponent(SHAPE);
60 - const targetObj = graphics.getObject(id);
61 -
62 - if (!targetObj) {
63 - return Promise.reject(rejectMessages.noObject);
64 - }
65 -
66 - if (!this.isRedo) {
67 - const undoData = makeUndoData(options, targetObj);
68 -
69 - chchedUndoDataForSilent = this.setUndoData(undoData, chchedUndoDataForSilent, isSilent);
70 - }
71 -
72 - return shapeComp.change(targetObj, options);
73 - },
74 - /**
75 - * @param {Graphics} graphics - Graphics instance
76 - * @returns {Promise}
77 - */
78 - undo(graphics) {
79 - const shapeComp = graphics.getComponent(SHAPE);
80 - const { object: shape, options } = this.undoData;
81 -
82 - return shapeComp.change(shape, options);
83 - },
84 -};
85 -
86 -commandFactory.register(command);
87 -
88 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Change a text
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { componentNames, rejectMessages, commandNames } from '../consts';
8 -
9 -const { TEXT } = componentNames;
10 -
11 -const command = {
12 - name: commandNames.CHANGE_TEXT,
13 -
14 - /**
15 - * Change a text
16 - * @param {Graphics} graphics - Graphics instance
17 - * @param {number} id - object id
18 - * @param {string} text - Changing text
19 - * @returns {Promise}
20 - */
21 - execute(graphics, id, text) {
22 - const textComp = graphics.getComponent(TEXT);
23 - const targetObj = graphics.getObject(id);
24 -
25 - if (!targetObj) {
26 - return Promise.reject(rejectMessages.noObject);
27 - }
28 -
29 - this.undoData.object = targetObj;
30 - this.undoData.text = textComp.getText(targetObj);
31 -
32 - return textComp.change(targetObj, text);
33 - },
34 - /**
35 - * @param {Graphics} graphics - Graphics instance
36 - * @returns {Promise}
37 - */
38 - undo(graphics) {
39 - const textComp = graphics.getComponent(TEXT);
40 - const { object: textObj, text } = this.undoData;
41 -
42 - return textComp.change(textObj, text);
43 - },
44 -};
45 -
46 -commandFactory.register(command);
47 -
48 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Change text styles
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import commandFactory from '../factory/command';
7 -import { Promise } from '../util';
8 -import { componentNames, rejectMessages, commandNames } from '../consts';
9 -
10 -const { TEXT } = componentNames;
11 -
12 -/**
13 - * Chched data for undo
14 - * @type {Object}
15 - */
16 -let chchedUndoDataForSilent = null;
17 -
18 -/**
19 - * Make undoData
20 - * @param {object} styles - text styles
21 - * @param {Component} targetObj - text component
22 - * @returns {object} - undo data
23 - */
24 -function makeUndoData(styles, targetObj) {
25 - const undoData = {
26 - object: targetObj,
27 - styles: {},
28 - };
29 - snippet.forEachOwnProperties(styles, (value, key) => {
30 - const undoValue = targetObj[key];
31 - undoData.styles[key] = undoValue;
32 - });
33 -
34 - return undoData;
35 -}
36 -
37 -const command = {
38 - name: commandNames.CHANGE_TEXT_STYLE,
39 -
40 - /**
41 - * Change text styles
42 - * @param {Graphics} graphics - Graphics instance
43 - * @param {number} id - object id
44 - * @param {Object} styles - text styles
45 - * @param {string} [styles.fill] Color
46 - * @param {string} [styles.fontFamily] Font type for text
47 - * @param {number} [styles.fontSize] Size
48 - * @param {string} [styles.fontStyle] Type of inclination (normal / italic)
49 - * @param {string} [styles.fontWeight] Type of thicker or thinner looking (normal / bold)
50 - * @param {string} [styles.textAlign] Type of text align (left / center / right)
51 - * @param {string} [styles.textDecoration] Type of line (underline / line-through / overline)
52 - * @param {boolean} isSilent - is silent execution or not
53 - * @returns {Promise}
54 - */
55 - execute(graphics, id, styles, isSilent) {
56 - const textComp = graphics.getComponent(TEXT);
57 - const targetObj = graphics.getObject(id);
58 -
59 - if (!targetObj) {
60 - return Promise.reject(rejectMessages.noObject);
61 - }
62 - if (!this.isRedo) {
63 - const undoData = makeUndoData(styles, targetObj);
64 -
65 - chchedUndoDataForSilent = this.setUndoData(undoData, chchedUndoDataForSilent, isSilent);
66 - }
67 -
68 - return textComp.setStyle(targetObj, styles);
69 - },
70 - /**
71 - * @param {Graphics} graphics - Graphics instance
72 - * @returns {Promise}
73 - */
74 - undo(graphics) {
75 - const textComp = graphics.getComponent(TEXT);
76 - const { object: textObj, styles } = this.undoData;
77 -
78 - return textComp.setStyle(textObj, styles);
79 - },
80 -};
81 -
82 -commandFactory.register(command);
83 -
84 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Clear all objects
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.CLEAR_OBJECTS,
11 -
12 - /**
13 - * Clear all objects without background (main) image
14 - * @param {Graphics} graphics - Graphics instance
15 - * @returns {Promise}
16 - */
17 - execute(graphics) {
18 - return new Promise((resolve) => {
19 - this.undoData.objects = graphics.removeAll();
20 - resolve();
21 - });
22 - },
23 - /**
24 - * @param {Graphics} graphics - Graphics instance
25 - * @returns {Promise}
26 - * @ignore
27 - */
28 - undo(graphics) {
29 - graphics.add(this.undoData.objects);
30 -
31 - return Promise.resolve();
32 - },
33 -};
34 -
35 -commandFactory.register(command);
36 -
37 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Flip an image
4 - */
5 -import commandFactory from '../factory/command';
6 -import { componentNames, commandNames } from '../consts';
7 -
8 -const { FLIP } = componentNames;
9 -
10 -const command = {
11 - name: commandNames.FLIP_IMAGE,
12 -
13 - /**
14 - * flip an image
15 - * @param {Graphics} graphics - Graphics instance
16 - * @param {string} type - 'flipX' or 'flipY' or 'reset'
17 - * @returns {Promise}
18 - */
19 - execute(graphics, type) {
20 - const flipComp = graphics.getComponent(FLIP);
21 -
22 - this.undoData.setting = flipComp.getCurrentSetting();
23 -
24 - return flipComp[type]();
25 - },
26 - /**
27 - * @param {Graphics} graphics - Graphics instance
28 - * @returns {Promise}
29 - */
30 - undo(graphics) {
31 - const flipComp = graphics.getComponent(FLIP);
32 -
33 - return flipComp.set(this.undoData.setting);
34 - },
35 -};
36 -
37 -commandFactory.register(command);
38 -
39 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Load a background (main) image
4 - */
5 -import commandFactory from '../factory/command';
6 -import { componentNames, commandNames } from '../consts';
7 -
8 -const { IMAGE_LOADER } = componentNames;
9 -
10 -const command = {
11 - name: commandNames.LOAD_IMAGE,
12 -
13 - /**
14 - * Load a background (main) image
15 - * @param {Graphics} graphics - Graphics instance
16 - * @param {string} imageName - Image name
17 - * @param {string} imgUrl - Image Url
18 - * @returns {Promise}
19 - */
20 - execute(graphics, imageName, imgUrl) {
21 - const loader = graphics.getComponent(IMAGE_LOADER);
22 - const prevImage = loader.getCanvasImage();
23 - const prevImageWidth = prevImage ? prevImage.width : 0;
24 - const prevImageHeight = prevImage ? prevImage.height : 0;
25 - const objects = graphics.removeAll(true).filter((objectItem) => objectItem.type !== 'cropzone');
26 -
27 - objects.forEach((objectItem) => {
28 - objectItem.evented = true;
29 - });
30 -
31 - this.undoData = {
32 - name: loader.getImageName(),
33 - image: prevImage,
34 - objects,
35 - };
36 -
37 - return loader.load(imageName, imgUrl).then((newImage) => ({
38 - oldWidth: prevImageWidth,
39 - oldHeight: prevImageHeight,
40 - newWidth: newImage.width,
41 - newHeight: newImage.height,
42 - }));
43 - },
44 -
45 - /**
46 - * @param {Graphics} graphics - Graphics instance
47 - * @returns {Promise}
48 - */
49 - undo(graphics) {
50 - const loader = graphics.getComponent(IMAGE_LOADER);
51 - const { objects, name, image } = this.undoData;
52 -
53 - graphics.removeAll(true);
54 - graphics.add(objects);
55 -
56 - return loader.load(name, image);
57 - },
58 -};
59 -
60 -commandFactory.register(command);
61 -
62 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Remove a filter from an image
4 - */
5 -import commandFactory from '../factory/command';
6 -import { componentNames, commandNames } from '../consts';
7 -
8 -const { FILTER } = componentNames;
9 -
10 -const command = {
11 - name: commandNames.REMOVE_FILTER,
12 -
13 - /**
14 - * Remove a filter from an image
15 - * @param {Graphics} graphics - Graphics instance
16 - * @param {string} type - Filter type
17 - * @returns {Promise}
18 - */
19 - execute(graphics, type) {
20 - const filterComp = graphics.getComponent(FILTER);
21 -
22 - this.undoData.options = filterComp.getOptions(type);
23 -
24 - return filterComp.remove(type);
25 - },
26 - /**
27 - * @param {Graphics} graphics - Graphics instance
28 - * @param {string} type - Filter type
29 - * @returns {Promise}
30 - */
31 - undo(graphics, type) {
32 - const filterComp = graphics.getComponent(FILTER);
33 - const { options } = this.undoData;
34 -
35 - return filterComp.add(type, options);
36 - },
37 -};
38 -
39 -commandFactory.register(command);
40 -
41 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Remove an object
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames, rejectMessages } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.REMOVE_OBJECT,
11 -
12 - /**
13 - * Remove an object
14 - * @param {Graphics} graphics - Graphics instance
15 - * @param {number} id - object id
16 - * @returns {Promise}
17 - */
18 - execute(graphics, id) {
19 - return new Promise((resolve, reject) => {
20 - this.undoData.objects = graphics.removeObjectById(id);
21 - if (this.undoData.objects.length) {
22 - resolve();
23 - } else {
24 - reject(rejectMessages.noObject);
25 - }
26 - });
27 - },
28 - /**
29 - * @param {Graphics} graphics - Graphics instance
30 - * @returns {Promise}
31 - */
32 - undo(graphics) {
33 - graphics.add(this.undoData.objects);
34 -
35 - return Promise.resolve();
36 - },
37 -};
38 -
39 -commandFactory.register(command);
40 -
41 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Resize a canvas
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.RESIZE_CANVAS_DIMENSION,
11 -
12 - /**
13 - * resize the canvas with given dimension
14 - * @param {Graphics} graphics - Graphics instance
15 - * @param {{width: number, height: number}} dimension - Max width & height
16 - * @returns {Promise}
17 - */
18 - execute(graphics, dimension) {
19 - return new Promise((resolve) => {
20 - this.undoData.size = {
21 - width: graphics.cssMaxWidth,
22 - height: graphics.cssMaxHeight,
23 - };
24 -
25 - graphics.setCssMaxDimension(dimension);
26 - graphics.adjustCanvasDimension();
27 - resolve();
28 - });
29 - },
30 - /**
31 - * @param {Graphics} graphics - Graphics instance
32 - * @returns {Promise}
33 - */
34 - undo(graphics) {
35 - graphics.setCssMaxDimension(this.undoData.size);
36 - graphics.adjustCanvasDimension();
37 -
38 - return Promise.resolve();
39 - },
40 -};
41 -
42 -commandFactory.register(command);
43 -
44 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Rotate an image
4 - */
5 -import commandFactory from '../factory/command';
6 -import { componentNames, commandNames } from '../consts';
7 -
8 -const { ROTATION } = componentNames;
9 -
10 -/**
11 - * Chched data for undo
12 - * @type {Object}
13 - */
14 -let chchedUndoDataForSilent = null;
15 -
16 -/**
17 - * Make undo data
18 - * @param {Component} rotationComp - rotation component
19 - * @returns {object} - undodata
20 - */
21 -function makeUndoData(rotationComp) {
22 - return {
23 - angle: rotationComp.getCurrentAngle(),
24 - };
25 -}
26 -
27 -const command = {
28 - name: commandNames.ROTATE_IMAGE,
29 -
30 - /**
31 - * Rotate an image
32 - * @param {Graphics} graphics - Graphics instance
33 - * @param {string} type - 'rotate' or 'setAngle'
34 - * @param {number} angle - angle value (degree)
35 - * @param {boolean} isSilent - is silent execution or not
36 - * @returns {Promise}
37 - */
38 - execute(graphics, type, angle, isSilent) {
39 - const rotationComp = graphics.getComponent(ROTATION);
40 -
41 - if (!this.isRedo) {
42 - const undoData = makeUndoData(rotationComp);
43 -
44 - chchedUndoDataForSilent = this.setUndoData(undoData, chchedUndoDataForSilent, isSilent);
45 - }
46 -
47 - return rotationComp[type](angle);
48 - },
49 - /**
50 - * @param {Graphics} graphics - Graphics instance
51 - * @returns {Promise}
52 - */
53 - undo(graphics) {
54 - const rotationComp = graphics.getComponent(ROTATION);
55 - const [, type, angle] = this.args;
56 -
57 - if (type === 'setAngle') {
58 - return rotationComp[type](this.undoData.angle);
59 - }
60 -
61 - return rotationComp.rotate(-angle);
62 - },
63 -};
64 -
65 -commandFactory.register(command);
66 -
67 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Set object properties
4 - */
5 -import commandFactory from '../factory/command';
6 -import { Promise } from '../util';
7 -import { commandNames, rejectMessages } from '../consts';
8 -
9 -const command = {
10 - name: commandNames.SET_OBJECT_POSITION,
11 -
12 - /**
13 - * Set object properties
14 - * @param {Graphics} graphics - Graphics instance
15 - * @param {number} id - object id
16 - * @param {Object} posInfo - position object
17 - * @param {number} posInfo.x - x position
18 - * @param {number} posInfo.y - y position
19 - * @param {string} posInfo.originX - can be 'left', 'center', 'right'
20 - * @param {string} posInfo.originY - can be 'top', 'center', 'bottom'
21 - * @returns {Promise}
22 - */
23 - execute(graphics, id, posInfo) {
24 - const targetObj = graphics.getObject(id);
25 -
26 - if (!targetObj) {
27 - return Promise.reject(rejectMessages.noObject);
28 - }
29 -
30 - this.undoData.objectId = id;
31 - this.undoData.props = graphics.getObjectProperties(id, ['left', 'top']);
32 -
33 - graphics.setObjectPosition(id, posInfo);
34 - graphics.renderAll();
35 -
36 - return Promise.resolve();
37 - },
38 - /**
39 - * @param {Graphics} graphics - Graphics instance
40 - * @returns {Promise}
41 - */
42 - undo(graphics) {
43 - const { objectId, props } = this.undoData;
44 -
45 - graphics.setObjectProperties(objectId, props);
46 - graphics.renderAll();
47 -
48 - return Promise.resolve();
49 - },
50 -};
51 -
52 -commandFactory.register(command);
53 -
54 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Set object properties
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import commandFactory from '../factory/command';
7 -import { Promise } from '../util';
8 -import { commandNames, rejectMessages } from '../consts';
9 -
10 -const command = {
11 - name: commandNames.SET_OBJECT_PROPERTIES,
12 -
13 - /**
14 - * Set object properties
15 - * @param {Graphics} graphics - Graphics instance
16 - * @param {number} id - object id
17 - * @param {Object} props - properties
18 - * @param {string} [props.fill] Color
19 - * @param {string} [props.fontFamily] Font type for text
20 - * @param {number} [props.fontSize] Size
21 - * @param {string} [props.fontStyle] Type of inclination (normal / italic)
22 - * @param {string} [props.fontWeight] Type of thicker or thinner looking (normal / bold)
23 - * @param {string} [props.textAlign] Type of text align (left / center / right)
24 - * @param {string} [props.textDecoration] Type of line (underline / line-through / overline)
25 - * @returns {Promise}
26 - */
27 - execute(graphics, id, props) {
28 - const targetObj = graphics.getObject(id);
29 -
30 - if (!targetObj) {
31 - return Promise.reject(rejectMessages.noObject);
32 - }
33 -
34 - this.undoData.props = {};
35 - snippet.forEachOwnProperties(props, (value, key) => {
36 - this.undoData.props[key] = targetObj[key];
37 - });
38 -
39 - graphics.setObjectProperties(id, props);
40 -
41 - return Promise.resolve();
42 - },
43 - /**
44 - * @param {Graphics} graphics - Graphics instance
45 - * @param {number} id - object id
46 - * @returns {Promise}
47 - */
48 - undo(graphics, id) {
49 - const { props } = this.undoData;
50 -
51 - graphics.setObjectProperties(id, props);
52 -
53 - return Promise.resolve();
54 - },
55 -};
56 -
57 -commandFactory.register(command);
58 -
59 -export default command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Image crop module (start cropping, end cropping)
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import fabric from 'fabric';
7 -import Component from '../interface/component';
8 -import Cropzone from '../extension/cropzone';
9 -import { keyCodes, componentNames, CROPZONE_DEFAULT_OPTIONS } from '../consts';
10 -import { clamp, fixFloatingPoint } from '../util';
11 -
12 -const MOUSE_MOVE_THRESHOLD = 10;
13 -const DEFAULT_OPTION = {
14 - presetRatio: null,
15 - top: -10,
16 - left: -10,
17 - height: 1,
18 - width: 1,
19 -};
20 -
21 -/**
22 - * Cropper components
23 - * @param {Graphics} graphics - Graphics instance
24 - * @extends {Component}
25 - * @class Cropper
26 - * @ignore
27 - */
28 -class Cropper extends Component {
29 - constructor(graphics) {
30 - super(componentNames.CROPPER, graphics);
31 -
32 - /**
33 - * Cropzone
34 - * @type {Cropzone}
35 - * @private
36 - */
37 - this._cropzone = null;
38 -
39 - /**
40 - * StartX of Cropzone
41 - * @type {number}
42 - * @private
43 - */
44 - this._startX = null;
45 -
46 - /**
47 - * StartY of Cropzone
48 - * @type {number}
49 - * @private
50 - */
51 - this._startY = null;
52 -
53 - /**
54 - * State whether shortcut key is pressed or not
55 - * @type {boolean}
56 - * @private
57 - */
58 - this._withShiftKey = false;
59 -
60 - /**
61 - * Listeners
62 - * @type {object.<string, function>}
63 - * @private
64 - */
65 - this._listeners = {
66 - keydown: this._onKeyDown.bind(this),
67 - keyup: this._onKeyUp.bind(this),
68 - mousedown: this._onFabricMouseDown.bind(this),
69 - mousemove: this._onFabricMouseMove.bind(this),
70 - mouseup: this._onFabricMouseUp.bind(this),
71 - };
72 - }
73 -
74 - /**
75 - * Start cropping
76 - */
77 - start() {
78 - if (this._cropzone) {
79 - return;
80 - }
81 - const canvas = this.getCanvas();
82 -
83 - canvas.forEachObject((obj) => {
84 - // {@link http://fabricjs.com/docs/fabric.Object.html#evented}
85 - obj.evented = false;
86 - });
87 -
88 - this._cropzone = new Cropzone(
89 - canvas,
90 - snippet.extend(
91 - {
92 - left: 0,
93 - top: 0,
94 - width: 0.5,
95 - height: 0.5,
96 - strokeWidth: 0, // {@link https://github.com/kangax/fabric.js/issues/2860}
97 - cornerSize: 10,
98 - cornerColor: 'black',
99 - fill: 'transparent',
100 - },
101 - CROPZONE_DEFAULT_OPTIONS,
102 - this.graphics.cropSelectionStyle
103 - )
104 - );
105 -
106 - canvas.discardActiveObject();
107 - canvas.add(this._cropzone);
108 - canvas.on('mouse:down', this._listeners.mousedown);
109 - canvas.selection = false;
110 - canvas.defaultCursor = 'crosshair';
111 -
112 - fabric.util.addListener(document, 'keydown', this._listeners.keydown);
113 - fabric.util.addListener(document, 'keyup', this._listeners.keyup);
114 - }
115 -
116 - /**
117 - * End cropping
118 - */
119 - end() {
120 - const canvas = this.getCanvas();
121 - const cropzone = this._cropzone;
122 -
123 - if (!cropzone) {
124 - return;
125 - }
126 - canvas.remove(cropzone);
127 - canvas.selection = true;
128 - canvas.defaultCursor = 'default';
129 - canvas.off('mouse:down', this._listeners.mousedown);
130 - canvas.forEachObject((obj) => {
131 - obj.evented = true;
132 - });
133 -
134 - this._cropzone = null;
135 -
136 - fabric.util.removeListener(document, 'keydown', this._listeners.keydown);
137 - fabric.util.removeListener(document, 'keyup', this._listeners.keyup);
138 - }
139 -
140 - /**
141 - * Change cropzone visible
142 - * @param {boolean} visible - cropzone visible state
143 - */
144 - changeVisibility(visible) {
145 - if (this._cropzone) {
146 - this._cropzone.set({ visible });
147 - }
148 - }
149 -
150 - /**
151 - * onMousedown handler in fabric canvas
152 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
153 - * @private
154 - */
155 - _onFabricMouseDown(fEvent) {
156 - const canvas = this.getCanvas();
157 -
158 - if (fEvent.target) {
159 - return;
160 - }
161 -
162 - canvas.selection = false;
163 - const coord = canvas.getPointer(fEvent.e);
164 -
165 - this._startX = coord.x;
166 - this._startY = coord.y;
167 -
168 - canvas.on({
169 - 'mouse:move': this._listeners.mousemove,
170 - 'mouse:up': this._listeners.mouseup,
171 - });
172 - }
173 -
174 - /**
175 - * onMousemove handler in fabric canvas
176 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
177 - * @private
178 - */
179 - _onFabricMouseMove(fEvent) {
180 - const canvas = this.getCanvas();
181 - const pointer = canvas.getPointer(fEvent.e);
182 - const { x, y } = pointer;
183 - const cropzone = this._cropzone;
184 -
185 - if (Math.abs(x - this._startX) + Math.abs(y - this._startY) > MOUSE_MOVE_THRESHOLD) {
186 - canvas.remove(cropzone);
187 - cropzone.set(this._calcRectDimensionFromPoint(x, y));
188 -
189 - canvas.add(cropzone);
190 - canvas.setActiveObject(cropzone);
191 - }
192 - }
193 -
194 - /**
195 - * Get rect dimension setting from Canvas-Mouse-Position(x, y)
196 - * @param {number} x - Canvas-Mouse-Position x
197 - * @param {number} y - Canvas-Mouse-Position Y
198 - * @returns {{left: number, top: number, width: number, height: number}}
199 - * @private
200 - */
201 - _calcRectDimensionFromPoint(x, y) {
202 - const canvas = this.getCanvas();
203 - const canvasWidth = canvas.getWidth();
204 - const canvasHeight = canvas.getHeight();
205 - const startX = this._startX;
206 - const startY = this._startY;
207 - let left = clamp(x, 0, startX);
208 - let top = clamp(y, 0, startY);
209 - let width = clamp(x, startX, canvasWidth) - left; // (startX <= x(mouse) <= canvasWidth) - left
210 - let height = clamp(y, startY, canvasHeight) - top; // (startY <= y(mouse) <= canvasHeight) - top
211 -
212 - if (this._withShiftKey) {
213 - // make fixed ratio cropzone
214 - if (width > height) {
215 - height = width;
216 - } else if (height > width) {
217 - width = height;
218 - }
219 -
220 - if (startX >= x) {
221 - left = startX - width;
222 - }
223 -
224 - if (startY >= y) {
225 - top = startY - height;
226 - }
227 - }
228 -
229 - return {
230 - left,
231 - top,
232 - width,
233 - height,
234 - };
235 - }
236 -
237 - /**
238 - * onMouseup handler in fabric canvas
239 - * @private
240 - */
241 - _onFabricMouseUp() {
242 - const cropzone = this._cropzone;
243 - const listeners = this._listeners;
244 - const canvas = this.getCanvas();
245 -
246 - canvas.setActiveObject(cropzone);
247 - canvas.off({
248 - 'mouse:move': listeners.mousemove,
249 - 'mouse:up': listeners.mouseup,
250 - });
251 - }
252 -
253 - /**
254 - * Get cropped image data
255 - * @param {Object} cropRect cropzone rect
256 - * @param {Number} cropRect.left left position
257 - * @param {Number} cropRect.top top position
258 - * @param {Number} cropRect.width width
259 - * @param {Number} cropRect.height height
260 - * @returns {?{imageName: string, url: string}} cropped Image data
261 - */
262 - getCroppedImageData(cropRect) {
263 - const canvas = this.getCanvas();
264 - const containsCropzone = canvas.contains(this._cropzone);
265 - if (!cropRect) {
266 - return null;
267 - }
268 -
269 - if (containsCropzone) {
270 - canvas.remove(this._cropzone);
271 - }
272 -
273 - const imageData = {
274 - imageName: this.getImageName(),
275 - url: canvas.toDataURL(cropRect),
276 - };
277 -
278 - if (containsCropzone) {
279 - canvas.add(this._cropzone);
280 - }
281 -
282 - return imageData;
283 - }
284 -
285 - /**
286 - * Get cropped rect
287 - * @returns {Object} rect
288 - */
289 - getCropzoneRect() {
290 - const cropzone = this._cropzone;
291 -
292 - if (!cropzone.isValid()) {
293 - return null;
294 - }
295 -
296 - return {
297 - left: cropzone.left,
298 - top: cropzone.top,
299 - width: cropzone.width,
300 - height: cropzone.height,
301 - };
302 - }
303 -
304 - /**
305 - * Set a cropzone square
306 - * @param {number} [presetRatio] - preset ratio
307 - */
308 - setCropzoneRect(presetRatio) {
309 - const canvas = this.getCanvas();
310 - const cropzone = this._cropzone;
311 -
312 - canvas.discardActiveObject();
313 - canvas.selection = false;
314 - canvas.remove(cropzone);
315 -
316 - cropzone.set(presetRatio ? this._getPresetPropertiesForCropSize(presetRatio) : DEFAULT_OPTION);
317 -
318 - canvas.add(cropzone);
319 - canvas.selection = true;
320 -
321 - if (presetRatio) {
322 - canvas.setActiveObject(cropzone);
323 - }
324 - }
325 -
326 - /**
327 - * get a cropzone square info
328 - * @param {number} presetRatio - preset ratio
329 - * @returns {{presetRatio: number, left: number, top: number, width: number, height: number}}
330 - * @private
331 - */
332 - _getPresetPropertiesForCropSize(presetRatio) {
333 - const canvas = this.getCanvas();
334 - const originalWidth = canvas.getWidth();
335 - const originalHeight = canvas.getHeight();
336 -
337 - const standardSize = originalWidth >= originalHeight ? originalWidth : originalHeight;
338 - const getScale = (value, orignalValue) => (value > orignalValue ? orignalValue / value : 1);
339 -
340 - let width = standardSize * presetRatio;
341 - let height = standardSize;
342 -
343 - const scaleWidth = getScale(width, originalWidth);
344 - [width, height] = snippet.map([width, height], (sizeValue) => sizeValue * scaleWidth);
345 -
346 - const scaleHeight = getScale(height, originalHeight);
347 - [width, height] = snippet.map([width, height], (sizeValue) =>
348 - fixFloatingPoint(sizeValue * scaleHeight)
349 - );
350 -
351 - return {
352 - presetRatio,
353 - top: (originalHeight - height) / 2,
354 - left: (originalWidth - width) / 2,
355 - width,
356 - height,
357 - };
358 - }
359 -
360 - /**
361 - * Keydown event handler
362 - * @param {KeyboardEvent} e - Event object
363 - * @private
364 - */
365 - _onKeyDown(e) {
366 - if (e.keyCode === keyCodes.SHIFT) {
367 - this._withShiftKey = true;
368 - }
369 - }
370 -
371 - /**
372 - * Keyup event handler
373 - * @param {KeyboardEvent} e - Event object
374 - * @private
375 - */
376 - _onKeyUp(e) {
377 - if (e.keyCode === keyCodes.SHIFT) {
378 - this._withShiftKey = false;
379 - }
380 - }
381 -}
382 -
383 -export default Cropper;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add filter module
4 - */
5 -import { isUndefined, extend, forEach, filter } from 'tui-code-snippet';
6 -import { Promise } from '../util';
7 -import fabric from 'fabric';
8 -import Component from '../interface/component';
9 -import Mask from '../extension/mask';
10 -import { rejectMessages, componentNames } from '../consts';
11 -import Sharpen from '../extension/sharpen';
12 -import Emboss from '../extension/emboss';
13 -import ColorFilter from '../extension/colorFilter';
14 -
15 -const { filters } = fabric.Image;
16 -filters.Mask = Mask;
17 -filters.Sharpen = Sharpen;
18 -filters.Emboss = Emboss;
19 -filters.ColorFilter = ColorFilter;
20 -
21 -/**
22 - * Filter
23 - * @class Filter
24 - * @param {Graphics} graphics - Graphics instance
25 - * @extends {Component}
26 - * @ignore
27 - */
28 -class Filter extends Component {
29 - constructor(graphics) {
30 - super(componentNames.FILTER, graphics);
31 - }
32 -
33 - /**
34 - * Add filter to source image (a specific filter is added on fabric.js)
35 - * @param {string} type - Filter type
36 - * @param {Object} [options] - Options of filter
37 - * @returns {Promise}
38 - */
39 - add(type, options) {
40 - return new Promise((resolve, reject) => {
41 - const sourceImg = this._getSourceImage();
42 - const canvas = this.getCanvas();
43 - let imgFilter = this._getFilter(sourceImg, type);
44 - if (!imgFilter) {
45 - imgFilter = this._createFilter(sourceImg, type, options);
46 - }
47 -
48 - if (!imgFilter) {
49 - reject(rejectMessages.invalidParameters);
50 - }
51 -
52 - this._changeFilterValues(imgFilter, options);
53 -
54 - this._apply(sourceImg, () => {
55 - canvas.renderAll();
56 - resolve({
57 - type,
58 - action: 'add',
59 - options,
60 - });
61 - });
62 - });
63 - }
64 -
65 - /**
66 - * Remove filter to source image
67 - * @param {string} type - Filter type
68 - * @returns {Promise}
69 - */
70 - remove(type) {
71 - return new Promise((resolve, reject) => {
72 - const sourceImg = this._getSourceImage();
73 - const canvas = this.getCanvas();
74 - const options = this.getOptions(type);
75 -
76 - if (!sourceImg.filters.length) {
77 - reject(rejectMessages.unsupportedOperation);
78 - }
79 -
80 - this._removeFilter(sourceImg, type);
81 -
82 - this._apply(sourceImg, () => {
83 - canvas.renderAll();
84 - resolve({
85 - type,
86 - action: 'remove',
87 - options,
88 - });
89 - });
90 - });
91 - }
92 -
93 - /**
94 - * Whether this has the filter or not
95 - * @param {string} type - Filter type
96 - * @returns {boolean} true if it has the filter
97 - */
98 - hasFilter(type) {
99 - return !!this._getFilter(this._getSourceImage(), type);
100 - }
101 -
102 - /**
103 - * Get a filter options
104 - * @param {string} type - Filter type
105 - * @returns {Object} filter options or null if there is no that filter
106 - */
107 - getOptions(type) {
108 - const sourceImg = this._getSourceImage();
109 - const imgFilter = this._getFilter(sourceImg, type);
110 - if (!imgFilter) {
111 - return null;
112 - }
113 -
114 - return extend({}, imgFilter.options);
115 - }
116 -
117 - /**
118 - * Change filter values
119 - * @param {Object} imgFilter object of filter
120 - * @param {Object} options object
121 - * @private
122 - */
123 - _changeFilterValues(imgFilter, options) {
124 - forEach(options, (value, key) => {
125 - if (!isUndefined(imgFilter[key])) {
126 - imgFilter[key] = value;
127 - }
128 - });
129 - forEach(imgFilter.options, (value, key) => {
130 - if (!isUndefined(options[key])) {
131 - imgFilter.options[key] = options[key];
132 - }
133 - });
134 - }
135 -
136 - /**
137 - * Apply filter
138 - * @param {fabric.Image} sourceImg - Source image to apply filter
139 - * @param {function} callback - Executed function after applying filter
140 - * @private
141 - */
142 - _apply(sourceImg, callback) {
143 - sourceImg.filters.push();
144 - const result = sourceImg.applyFilters();
145 - if (result) {
146 - callback();
147 - }
148 - }
149 -
150 - /**
151 - * Get source image on canvas
152 - * @returns {fabric.Image} Current source image on canvas
153 - * @private
154 - */
155 - _getSourceImage() {
156 - return this.getCanvasImage();
157 - }
158 -
159 - /**
160 - * Create filter instance
161 - * @param {fabric.Image} sourceImg - Source image to apply filter
162 - * @param {string} type - Filter type
163 - * @param {Object} [options] - Options of filter
164 - * @returns {Object} Fabric object of filter
165 - * @private
166 - */
167 - _createFilter(sourceImg, type, options) {
168 - let filterObj;
169 - // capitalize first letter for matching with fabric image filter name
170 - const fabricType = this._getFabricFilterType(type);
171 - const ImageFilter = fabric.Image.filters[fabricType];
172 - if (ImageFilter) {
173 - filterObj = new ImageFilter(options);
174 - filterObj.options = options;
175 - sourceImg.filters.push(filterObj);
176 - }
177 -
178 - return filterObj;
179 - }
180 -
181 - /**
182 - * Get applied filter instance
183 - * @param {fabric.Image} sourceImg - Source image to apply filter
184 - * @param {string} type - Filter type
185 - * @returns {Object} Fabric object of filter
186 - * @private
187 - */
188 - _getFilter(sourceImg, type) {
189 - let imgFilter = null;
190 -
191 - if (sourceImg) {
192 - const fabricType = this._getFabricFilterType(type);
193 - const { length } = sourceImg.filters;
194 - let item, i;
195 -
196 - for (i = 0; i < length; i += 1) {
197 - item = sourceImg.filters[i];
198 - if (item.type === fabricType) {
199 - imgFilter = item;
200 - break;
201 - }
202 - }
203 - }
204 -
205 - return imgFilter;
206 - }
207 -
208 - /**
209 - * Remove applied filter instance
210 - * @param {fabric.Image} sourceImg - Source image to apply filter
211 - * @param {string} type - Filter type
212 - * @private
213 - */
214 - _removeFilter(sourceImg, type) {
215 - const fabricType = this._getFabricFilterType(type);
216 - sourceImg.filters = filter(sourceImg.filters, (value) => value.type !== fabricType);
217 - }
218 -
219 - /**
220 - * Change filter class name to fabric's, especially capitalizing first letter
221 - * @param {string} type - Filter type
222 - * @example
223 - * 'grayscale' -> 'Grayscale'
224 - * @returns {string} Fabric filter class name
225 - */
226 - _getFabricFilterType(type) {
227 - return type.charAt(0).toUpperCase() + type.slice(1);
228 - }
229 -}
230 -
231 -export default Filter;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Image flip module
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import { Promise } from '../util';
7 -import Component from '../interface/component';
8 -import { componentNames, rejectMessages } from '../consts';
9 -
10 -/**
11 - * Flip
12 - * @class Flip
13 - * @param {Graphics} graphics - Graphics instance
14 - * @extends {Component}
15 - * @ignore
16 - */
17 -class Flip extends Component {
18 - constructor(graphics) {
19 - super(componentNames.FLIP, graphics);
20 - }
21 -
22 - /**
23 - * Get current flip settings
24 - * @returns {{flipX: Boolean, flipY: Boolean}}
25 - */
26 - getCurrentSetting() {
27 - const canvasImage = this.getCanvasImage();
28 -
29 - return {
30 - flipX: canvasImage.flipX,
31 - flipY: canvasImage.flipY,
32 - };
33 - }
34 -
35 - /**
36 - * Set flipX, flipY
37 - * @param {{flipX: Boolean, flipY: Boolean}} newSetting - Flip setting
38 - * @returns {Promise}
39 - */
40 - set(newSetting) {
41 - const setting = this.getCurrentSetting();
42 - const isChangingFlipX = setting.flipX !== newSetting.flipX;
43 - const isChangingFlipY = setting.flipY !== newSetting.flipY;
44 -
45 - if (!isChangingFlipX && !isChangingFlipY) {
46 - return Promise.reject(rejectMessages.flip);
47 - }
48 -
49 - snippet.extend(setting, newSetting);
50 - this.setImageProperties(setting, true);
51 - this._invertAngle(isChangingFlipX, isChangingFlipY);
52 - this._flipObjects(isChangingFlipX, isChangingFlipY);
53 -
54 - return Promise.resolve({
55 - flipX: setting.flipX,
56 - flipY: setting.flipY,
57 - angle: this.getCanvasImage().angle,
58 - });
59 - }
60 -
61 - /**
62 - * Invert image angle for flip
63 - * @param {boolean} isChangingFlipX - Change flipX
64 - * @param {boolean} isChangingFlipY - Change flipY
65 - */
66 - _invertAngle(isChangingFlipX, isChangingFlipY) {
67 - const canvasImage = this.getCanvasImage();
68 - let { angle } = canvasImage;
69 -
70 - if (isChangingFlipX) {
71 - angle *= -1;
72 - }
73 - if (isChangingFlipY) {
74 - angle *= -1;
75 - }
76 - canvasImage.rotate(parseFloat(angle)).setCoords(); // parseFloat for -0 to 0
77 - }
78 -
79 - /**
80 - * Flip objects
81 - * @param {boolean} isChangingFlipX - Change flipX
82 - * @param {boolean} isChangingFlipY - Change flipY
83 - * @private
84 - */
85 - _flipObjects(isChangingFlipX, isChangingFlipY) {
86 - const canvas = this.getCanvas();
87 -
88 - if (isChangingFlipX) {
89 - canvas.forEachObject((obj) => {
90 - obj
91 - .set({
92 - angle: parseFloat(obj.angle * -1), // parseFloat for -0 to 0
93 - flipX: !obj.flipX,
94 - left: canvas.width - obj.left,
95 - })
96 - .setCoords();
97 - });
98 - }
99 - if (isChangingFlipY) {
100 - canvas.forEachObject((obj) => {
101 - obj
102 - .set({
103 - angle: parseFloat(obj.angle * -1), // parseFloat for -0 to 0
104 - flipY: !obj.flipY,
105 - top: canvas.height - obj.top,
106 - })
107 - .setCoords();
108 - });
109 - }
110 - canvas.renderAll();
111 - }
112 -
113 - /**
114 - * Reset flip settings
115 - * @returns {Promise}
116 - */
117 - reset() {
118 - return this.set({
119 - flipX: false,
120 - flipY: false,
121 - });
122 - }
123 -
124 - /**
125 - * Flip x
126 - * @returns {Promise}
127 - */
128 - flipX() {
129 - const current = this.getCurrentSetting();
130 -
131 - return this.set({
132 - flipX: !current.flipX,
133 - flipY: current.flipY,
134 - });
135 - }
136 -
137 - /**
138 - * Flip y
139 - * @returns {Promise}
140 - */
141 - flipY() {
142 - const current = this.getCurrentSetting();
143 -
144 - return this.set({
145 - flipX: current.flipX,
146 - flipY: !current.flipY,
147 - });
148 - }
149 -}
150 -
151 -export default Flip;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Free drawing module, Set brush
4 - */
5 -import fabric from 'fabric';
6 -import Component from '../interface/component';
7 -import { componentNames } from '../consts';
8 -
9 -/**
10 - * FreeDrawing
11 - * @class FreeDrawing
12 - * @param {Graphics} graphics - Graphics instance
13 - * @extends {Component}
14 - * @ignore
15 - */
16 -class FreeDrawing extends Component {
17 - constructor(graphics) {
18 - super(componentNames.FREE_DRAWING, graphics);
19 -
20 - /**
21 - * Brush width
22 - * @type {number}
23 - */
24 - this.width = 12;
25 -
26 - /**
27 - * fabric.Color instance for brush color
28 - * @type {fabric.Color}
29 - */
30 - this.oColor = new fabric.Color('rgba(0, 0, 0, 0.5)');
31 - }
32 -
33 - /**
34 - * Start free drawing mode
35 - * @param {{width: ?number, color: ?string}} [setting] - Brush width & color
36 - */
37 - start(setting) {
38 - const canvas = this.getCanvas();
39 -
40 - canvas.isDrawingMode = true;
41 - this.setBrush(setting);
42 - }
43 -
44 - /**
45 - * Set brush
46 - * @param {{width: ?number, color: ?string}} [setting] - Brush width & color
47 - */
48 - setBrush(setting) {
49 - const brush = this.getCanvas().freeDrawingBrush;
50 -
51 - setting = setting || {};
52 - this.width = setting.width || this.width;
53 - if (setting.color) {
54 - this.oColor = new fabric.Color(setting.color);
55 - }
56 - brush.width = this.width;
57 - brush.color = this.oColor.toRgba();
58 - }
59 -
60 - /**
61 - * End free drawing mode
62 - */
63 - end() {
64 - const canvas = this.getCanvas();
65 -
66 - canvas.isDrawingMode = false;
67 - }
68 -}
69 -
70 -export default FreeDrawing;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Add icon module
4 - */
5 -import fabric from 'fabric';
6 -import snippet from 'tui-code-snippet';
7 -import { Promise } from '../util';
8 -import Component from '../interface/component';
9 -import { eventNames as events, rejectMessages, componentNames, fObjectOptions } from '../consts';
10 -
11 -const pathMap = {
12 - arrow: 'M 0 90 H 105 V 120 L 160 60 L 105 0 V 30 H 0 Z',
13 - cancel:
14 - 'M 0 30 L 30 60 L 0 90 L 30 120 L 60 90 L 90 120 L 120 90 ' +
15 - 'L 90 60 L 120 30 L 90 0 L 60 30 L 30 0 Z',
16 -};
17 -
18 -/**
19 - * Icon
20 - * @class Icon
21 - * @param {Graphics} graphics - Graphics instance
22 - * @extends {Component}
23 - * @ignore
24 - */
25 -class Icon extends Component {
26 - constructor(graphics) {
27 - super(componentNames.ICON, graphics);
28 -
29 - /**
30 - * Default icon color
31 - * @type {string}
32 - */
33 - this._oColor = '#000000';
34 -
35 - /**
36 - * Path value of each icon type
37 - * @type {Object}
38 - */
39 - this._pathMap = pathMap;
40 -
41 - /**
42 - * Type of the drawing icon
43 - * @type {string}
44 - * @private
45 - */
46 - this._type = null;
47 -
48 - /**
49 - * Color of the drawing icon
50 - * @type {string}
51 - * @private
52 - */
53 - this._iconColor = null;
54 -
55 - /**
56 - * Event handler list
57 - * @type {Object}
58 - * @private
59 - */
60 - this._handlers = {
61 - mousedown: this._onFabricMouseDown.bind(this),
62 - mousemove: this._onFabricMouseMove.bind(this),
63 - mouseup: this._onFabricMouseUp.bind(this),
64 - };
65 - }
66 -
67 - /**
68 - * Set states of the current drawing shape
69 - * @ignore
70 - * @param {string} type - Icon type ('arrow', 'cancel', custom icon name)
71 - * @param {string} iconColor - Icon foreground color
72 - */
73 - setStates(type, iconColor) {
74 - this._type = type;
75 - this._iconColor = iconColor;
76 - }
77 -
78 - /**
79 - * Start to draw the icon on canvas
80 - * @ignore
81 - */
82 - start() {
83 - const canvas = this.getCanvas();
84 - canvas.selection = false;
85 - canvas.on('mouse:down', this._handlers.mousedown);
86 - }
87 -
88 - /**
89 - * End to draw the icon on canvas
90 - * @ignore
91 - */
92 - end() {
93 - const canvas = this.getCanvas();
94 -
95 - canvas.selection = true;
96 - canvas.off({
97 - 'mouse:down': this._handlers.mousedown,
98 - });
99 - }
100 -
101 - /**
102 - * Add icon
103 - * @param {string} type - Icon type
104 - * @param {Object} options - Icon options
105 - * @param {string} [options.fill] - Icon foreground color
106 - * @param {string} [options.left] - Icon x position
107 - * @param {string} [options.top] - Icon y position
108 - * @returns {Promise}
109 - */
110 - add(type, options) {
111 - return new Promise((resolve, reject) => {
112 - const canvas = this.getCanvas();
113 - const path = this._pathMap[type];
114 - const selectionStyle = fObjectOptions.SELECTION_STYLE;
115 - const icon = path ? this._createIcon(path) : null;
116 - this._icon = icon;
117 -
118 - if (!icon) {
119 - reject(rejectMessages.invalidParameters);
120 - }
121 -
122 - icon.set(
123 - snippet.extend(
124 - {
125 - type: 'icon',
126 - fill: this._oColor,
127 - },
128 - selectionStyle,
129 - options,
130 - this.graphics.controlStyle
131 - )
132 - );
133 -
134 - canvas.add(icon).setActiveObject(icon);
135 -
136 - resolve(this.graphics.createObjectProperties(icon));
137 - });
138 - }
139 -
140 - /**
141 - * Register icon paths
142 - * @param {{key: string, value: string}} pathInfos - Path infos
143 - */
144 - registerPaths(pathInfos) {
145 - snippet.forEach(
146 - pathInfos,
147 - (path, type) => {
148 - this._pathMap[type] = path;
149 - },
150 - this
151 - );
152 - }
153 -
154 - /**
155 - * Set icon object color
156 - * @param {string} color - Color to set
157 - * @param {fabric.Path}[obj] - Current activated path object
158 - */
159 - setColor(color, obj) {
160 - this._oColor = color;
161 -
162 - if (obj && obj.get('type') === 'icon') {
163 - obj.set({ fill: this._oColor });
164 - this.getCanvas().renderAll();
165 - }
166 - }
167 -
168 - /**
169 - * Get icon color
170 - * @param {fabric.Path}[obj] - Current activated path object
171 - * @returns {string} color
172 - */
173 - getColor(obj) {
174 - return obj.fill;
175 - }
176 -
177 - /**
178 - * Create icon object
179 - * @param {string} path - Path value to create icon
180 - * @returns {fabric.Path} Path object
181 - */
182 - _createIcon(path) {
183 - return new fabric.Path(path);
184 - }
185 -
186 - /**
187 - * MouseDown event handler on canvas
188 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
189 - * @private
190 - */
191 - _onFabricMouseDown(fEvent) {
192 - const canvas = this.getCanvas();
193 -
194 - this._startPoint = canvas.getPointer(fEvent.e);
195 - const { x: left, y: top } = this._startPoint;
196 -
197 - this.add(this._type, {
198 - left,
199 - top,
200 - fill: this._iconColor,
201 - }).then(() => {
202 - this.fire(events.ADD_OBJECT, this.graphics.createObjectProperties(this._icon));
203 - canvas.on('mouse:move', this._handlers.mousemove);
204 - canvas.on('mouse:up', this._handlers.mouseup);
205 - });
206 - }
207 -
208 - /**
209 - * MouseMove event handler on canvas
210 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
211 - * @private
212 - */
213 - _onFabricMouseMove(fEvent) {
214 - const canvas = this.getCanvas();
215 -
216 - if (!this._icon) {
217 - return;
218 - }
219 - const moveOriginPointer = canvas.getPointer(fEvent.e);
220 -
221 - const scaleX = (moveOriginPointer.x - this._startPoint.x) / this._icon.width;
222 - const scaleY = (moveOriginPointer.y - this._startPoint.y) / this._icon.height;
223 -
224 - this._icon.set({
225 - scaleX: Math.abs(scaleX * 2),
226 - scaleY: Math.abs(scaleY * 2),
227 - });
228 -
229 - this._icon.setCoords();
230 - canvas.renderAll();
231 - }
232 -
233 - /**
234 - * MouseUp event handler on canvas
235 - * @private
236 - */
237 - _onFabricMouseUp() {
238 - const canvas = this.getCanvas();
239 -
240 - this.fire(events.OBJECT_ADDED, this.graphics.createObjectProperties(this._icon));
241 -
242 - this._icon = null;
243 -
244 - canvas.off('mouse:down', this._handlers.mousedown);
245 - canvas.off('mouse:move', this._handlers.mousemove);
246 - canvas.off('mouse:up', this._handlers.mouseup);
247 - }
248 -}
249 -
250 -export default Icon;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Image loader
4 - */
5 -import Component from '../interface/component';
6 -import { componentNames, rejectMessages } from '../consts';
7 -import { Promise } from '../util';
8 -
9 -const imageOption = {
10 - padding: 0,
11 - crossOrigin: 'Anonymous',
12 -};
13 -
14 -/**
15 - * ImageLoader components
16 - * @extends {Component}
17 - * @class ImageLoader
18 - * @param {Graphics} graphics - Graphics instance
19 - * @ignore
20 - */
21 -class ImageLoader extends Component {
22 - constructor(graphics) {
23 - super(componentNames.IMAGE_LOADER, graphics);
24 - }
25 -
26 - /**
27 - * Load image from url
28 - * @param {?string} imageName - File name
29 - * @param {?(fabric.Image|string)} img - fabric.Image instance or URL of an image
30 - * @returns {Promise}
31 - */
32 - load(imageName, img) {
33 - let promise;
34 -
35 - if (!imageName && !img) {
36 - // Back to the initial state, not error.
37 - const canvas = this.getCanvas();
38 -
39 - canvas.backgroundImage = null;
40 - canvas.renderAll();
41 -
42 - promise = new Promise((resolve) => {
43 - this.setCanvasImage('', null);
44 - resolve();
45 - });
46 - } else {
47 - promise = this._setBackgroundImage(img).then((oImage) => {
48 - this.setCanvasImage(imageName, oImage);
49 - this.adjustCanvasDimension();
50 -
51 - return oImage;
52 - });
53 - }
54 -
55 - return promise;
56 - }
57 -
58 - /**
59 - * Set background image
60 - * @param {?(fabric.Image|String)} img fabric.Image instance or URL of an image to set background to
61 - * @returns {Promise}
62 - * @private
63 - */
64 - _setBackgroundImage(img) {
65 - if (!img) {
66 - return Promise.reject(rejectMessages.loadImage);
67 - }
68 -
69 - return new Promise((resolve, reject) => {
70 - const canvas = this.getCanvas();
71 -
72 - canvas.setBackgroundImage(
73 - img,
74 - () => {
75 - const oImage = canvas.backgroundImage;
76 -
77 - if (oImage && oImage.getElement()) {
78 - resolve(oImage);
79 - } else {
80 - reject(rejectMessages.loadingImageFailed);
81 - }
82 - },
83 - imageOption
84 - );
85 - });
86 - }
87 -}
88 -
89 -export default ImageLoader;
1 -/**
2 - * @author NHN. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Free drawing module, Set brush
4 - */
5 -import fabric from 'fabric';
6 -import snippet from 'tui-code-snippet';
7 -import Component from '../interface/component';
8 -import ArrowLine from '../extension/arrowLine';
9 -import { eventNames, componentNames, fObjectOptions } from '../consts';
10 -
11 -/**
12 - * Line
13 - * @class Line
14 - * @param {Graphics} graphics - Graphics instance
15 - * @extends {Component}
16 - * @ignore
17 - */
18 -class Line extends Component {
19 - constructor(graphics) {
20 - super(componentNames.LINE, graphics);
21 -
22 - /**
23 - * Brush width
24 - * @type {number}
25 - * @private
26 - */
27 - this._width = 12;
28 -
29 - /**
30 - * fabric.Color instance for brush color
31 - * @type {fabric.Color}
32 - * @private
33 - */
34 - this._oColor = new fabric.Color('rgba(0, 0, 0, 0.5)');
35 -
36 - /**
37 - * Listeners
38 - * @type {object.<string, function>}
39 - * @private
40 - */
41 - this._listeners = {
42 - mousedown: this._onFabricMouseDown.bind(this),
43 - mousemove: this._onFabricMouseMove.bind(this),
44 - mouseup: this._onFabricMouseUp.bind(this),
45 - };
46 - }
47 -
48 - /**
49 - * Start drawing line mode
50 - * @param {{width: ?number, color: ?string}} [setting] - Brush width & color
51 - */
52 - setHeadOption(setting) {
53 - const {
54 - arrowType = {
55 - head: null,
56 - tail: null,
57 - },
58 - } = setting;
59 -
60 - this._arrowType = arrowType;
61 - }
62 -
63 - /**
64 - * Start drawing line mode
65 - * @param {{width: ?number, color: ?string}} [setting] - Brush width & color
66 - */
67 - start(setting = {}) {
68 - const canvas = this.getCanvas();
69 -
70 - canvas.defaultCursor = 'crosshair';
71 - canvas.selection = false;
72 -
73 - this.setHeadOption(setting);
74 - this.setBrush(setting);
75 -
76 - canvas.forEachObject((obj) => {
77 - obj.set({
78 - evented: false,
79 - });
80 - });
81 -
82 - canvas.on({
83 - 'mouse:down': this._listeners.mousedown,
84 - });
85 - }
86 -
87 - /**
88 - * Set brush
89 - * @param {{width: ?number, color: ?string}} [setting] - Brush width & color
90 - */
91 - setBrush(setting) {
92 - const brush = this.getCanvas().freeDrawingBrush;
93 -
94 - setting = setting || {};
95 - this._width = setting.width || this._width;
96 -
97 - if (setting.color) {
98 - this._oColor = new fabric.Color(setting.color);
99 - }
100 - brush.width = this._width;
101 - brush.color = this._oColor.toRgba();
102 - }
103 -
104 - /**
105 - * End drawing line mode
106 - */
107 - end() {
108 - const canvas = this.getCanvas();
109 -
110 - canvas.defaultCursor = 'default';
111 - canvas.selection = true;
112 -
113 - canvas.forEachObject((obj) => {
114 - obj.set({
115 - evented: true,
116 - });
117 - });
118 -
119 - canvas.off('mouse:down', this._listeners.mousedown);
120 - }
121 -
122 - /**
123 - * Mousedown event handler in fabric canvas
124 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
125 - * @private
126 - */
127 - _onFabricMouseDown(fEvent) {
128 - const canvas = this.getCanvas();
129 - const { x, y } = canvas.getPointer(fEvent.e);
130 - const points = [x, y, x, y];
131 -
132 - this._line = new ArrowLine(points, {
133 - stroke: this._oColor.toRgba(),
134 - strokeWidth: this._width,
135 - arrowType: this._arrowType,
136 - evented: false,
137 - });
138 -
139 - this._line.set(fObjectOptions.SELECTION_STYLE);
140 -
141 - canvas.add(this._line);
142 -
143 - canvas.on({
144 - 'mouse:move': this._listeners.mousemove,
145 - 'mouse:up': this._listeners.mouseup,
146 - });
147 -
148 - this.fire(eventNames.ADD_OBJECT, this._createLineEventObjectProperties());
149 - }
150 -
151 - /**
152 - * Mousemove event handler in fabric canvas
153 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
154 - * @private
155 - */
156 - _onFabricMouseMove(fEvent) {
157 - const canvas = this.getCanvas();
158 - const pointer = canvas.getPointer(fEvent.e);
159 -
160 - this._line.set({
161 - x2: pointer.x,
162 - y2: pointer.y,
163 - });
164 -
165 - this._line.setCoords();
166 -
167 - canvas.renderAll();
168 - }
169 -
170 - /**
171 - * Mouseup event handler in fabric canvas
172 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
173 - * @private
174 - */
175 - _onFabricMouseUp() {
176 - const canvas = this.getCanvas();
177 -
178 - this.fire(eventNames.OBJECT_ADDED, this._createLineEventObjectProperties());
179 -
180 - this._line = null;
181 -
182 - canvas.off({
183 - 'mouse:move': this._listeners.mousemove,
184 - 'mouse:up': this._listeners.mouseup,
185 - });
186 - }
187 -
188 - /**
189 - * create line event object properties
190 - * @returns {Object} properties line object
191 - * @private
192 - */
193 - _createLineEventObjectProperties() {
194 - const params = this.graphics.createObjectProperties(this._line);
195 - const { x1, x2, y1, y2 } = this._line;
196 -
197 - return snippet.extend({}, params, {
198 - startPosition: {
199 - x: x1,
200 - y: y1,
201 - },
202 - endPosition: {
203 - x: x2,
204 - y: y2,
205 - },
206 - });
207 - }
208 -}
209 -
210 -export default Line;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Image rotation module
4 - */
5 -import fabric from 'fabric';
6 -import { Promise } from '../util';
7 -import Component from '../interface/component';
8 -import { componentNames } from '../consts';
9 -
10 -/**
11 - * Image Rotation component
12 - * @class Rotation
13 - * @extends {Component}
14 - * @param {Graphics} graphics - Graphics instance
15 - * @ignore
16 - */
17 -class Rotation extends Component {
18 - constructor(graphics) {
19 - super(componentNames.ROTATION, graphics);
20 - }
21 -
22 - /**
23 - * Get current angle
24 - * @returns {Number}
25 - */
26 - getCurrentAngle() {
27 - return this.getCanvasImage().angle;
28 - }
29 -
30 - /**
31 - * Set angle of the image
32 - *
33 - * Do not call "this.setImageProperties" for setting angle directly.
34 - * Before setting angle, The originX,Y of image should be set to center.
35 - * See "http://fabricjs.com/docs/fabric.Object.html#setAngle"
36 - *
37 - * @param {number} angle - Angle value
38 - * @returns {Promise}
39 - */
40 - setAngle(angle) {
41 - const oldAngle = this.getCurrentAngle() % 360; // The angle is lower than 2*PI(===360 degrees)
42 -
43 - angle %= 360;
44 -
45 - const canvasImage = this.getCanvasImage();
46 - const oldImageCenter = canvasImage.getCenterPoint();
47 - canvasImage.set({ angle }).setCoords();
48 - this.adjustCanvasDimension();
49 - const newImageCenter = canvasImage.getCenterPoint();
50 - this._rotateForEachObject(oldImageCenter, newImageCenter, angle - oldAngle);
51 -
52 - return Promise.resolve(angle);
53 - }
54 -
55 - /**
56 - * Rotate for each object
57 - * @param {fabric.Point} oldImageCenter - Image center point before rotation
58 - * @param {fabric.Point} newImageCenter - Image center point after rotation
59 - * @param {number} angleDiff - Image angle difference after rotation
60 - * @private
61 - */
62 - _rotateForEachObject(oldImageCenter, newImageCenter, angleDiff) {
63 - const canvas = this.getCanvas();
64 - const centerDiff = {
65 - x: oldImageCenter.x - newImageCenter.x,
66 - y: oldImageCenter.y - newImageCenter.y,
67 - };
68 -
69 - canvas.forEachObject((obj) => {
70 - const objCenter = obj.getCenterPoint();
71 - const radian = fabric.util.degreesToRadians(angleDiff);
72 - const newObjCenter = fabric.util.rotatePoint(objCenter, oldImageCenter, radian);
73 -
74 - obj.set({
75 - left: newObjCenter.x - centerDiff.x,
76 - top: newObjCenter.y - centerDiff.y,
77 - angle: (obj.angle + angleDiff) % 360,
78 - });
79 - obj.setCoords();
80 - });
81 - canvas.renderAll();
82 - }
83 -
84 - /**
85 - * Rotate the image
86 - * @param {number} additionalAngle - Additional angle
87 - * @returns {Promise}
88 - */
89 - rotate(additionalAngle) {
90 - const current = this.getCurrentAngle();
91 -
92 - return this.setAngle(current + additionalAngle);
93 - }
94 -}
95 -
96 -export default Rotation;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Shape component
4 - */
5 -import fabric from 'fabric';
6 -import Component from '../interface/component';
7 -import {
8 - rejectMessages,
9 - eventNames,
10 - keyCodes as KEY_CODES,
11 - componentNames,
12 - fObjectOptions,
13 - SHAPE_DEFAULT_OPTIONS,
14 - SHAPE_FILL_TYPE,
15 -} from '../consts';
16 -import resizeHelper from '../helper/shapeResizeHelper';
17 -import {
18 - getFillImageFromShape,
19 - rePositionFilterTypeFillImage,
20 - reMakePatternImageSource,
21 - makeFillPatternForFilter,
22 - makeFilterOptionFromFabricImage,
23 - resetFillPatternCanvas,
24 -} from '../helper/shapeFilterFillHelper';
25 -import {
26 - Promise,
27 - changeOrigin,
28 - getCustomProperty,
29 - getFillTypeFromOption,
30 - getFillTypeFromObject,
31 - isShape,
32 -} from '../util';
33 -import { extend } from 'tui-code-snippet';
34 -
35 -const SHAPE_INIT_OPTIONS = extend(
36 - {
37 - strokeWidth: 1,
38 - stroke: '#000000',
39 - fill: '#ffffff',
40 - width: 1,
41 - height: 1,
42 - rx: 0,
43 - ry: 0,
44 - },
45 - SHAPE_DEFAULT_OPTIONS
46 -);
47 -
48 -const DEFAULT_TYPE = 'rect';
49 -const DEFAULT_WIDTH = 20;
50 -const DEFAULT_HEIGHT = 20;
51 -
52 -/**
53 - * Make fill option
54 - * @param {Object} options - Options to create the shape
55 - * @param {Object.Image} canvasImage - canvas background image
56 - * @param {Function} createStaticCanvas - static canvas creater
57 - * @returns {Object} - shape option
58 - * @private
59 - */
60 -function makeFabricFillOption(options, canvasImage, createStaticCanvas) {
61 - const fillOption = options.fill;
62 - const fillType = getFillTypeFromOption(options.fill);
63 - let fill = fillOption;
64 -
65 - if (fillOption.color) {
66 - fill = fillOption.color;
67 - }
68 -
69 - let extOption = null;
70 - if (fillType === 'filter') {
71 - const newStaticCanvas = createStaticCanvas();
72 - extOption = makeFillPatternForFilter(canvasImage, fillOption.filter, newStaticCanvas);
73 - } else {
74 - extOption = { fill };
75 - }
76 -
77 - return extend({}, options, extOption);
78 -}
79 -
80 -/**
81 - * Shape
82 - * @class Shape
83 - * @param {Graphics} graphics - Graphics instance
84 - * @extends {Component}
85 - * @ignore
86 - */
87 -export default class Shape extends Component {
88 - constructor(graphics) {
89 - super(componentNames.SHAPE, graphics);
90 -
91 - /**
92 - * Object of The drawing shape
93 - * @type {fabric.Object}
94 - * @private
95 - */
96 - this._shapeObj = null;
97 -
98 - /**
99 - * Type of the drawing shape
100 - * @type {string}
101 - * @private
102 - */
103 - this._type = DEFAULT_TYPE;
104 -
105 - /**
106 - * Options to draw the shape
107 - * @type {Object}
108 - * @private
109 - */
110 - this._options = extend({}, SHAPE_INIT_OPTIONS);
111 -
112 - /**
113 - * Whether the shape object is selected or not
114 - * @type {boolean}
115 - * @private
116 - */
117 - this._isSelected = false;
118 -
119 - /**
120 - * Pointer for drawing shape (x, y)
121 - * @type {Object}
122 - * @private
123 - */
124 - this._startPoint = {};
125 -
126 - /**
127 - * Using shortcut on drawing shape
128 - * @type {boolean}
129 - * @private
130 - */
131 - this._withShiftKey = false;
132 -
133 - /**
134 - * Event handler list
135 - * @type {Object}
136 - * @private
137 - */
138 - this._handlers = {
139 - mousedown: this._onFabricMouseDown.bind(this),
140 - mousemove: this._onFabricMouseMove.bind(this),
141 - mouseup: this._onFabricMouseUp.bind(this),
142 - keydown: this._onKeyDown.bind(this),
143 - keyup: this._onKeyUp.bind(this),
144 - };
145 - }
146 -
147 - /**
148 - * Start to draw the shape on canvas
149 - * @ignore
150 - */
151 - start() {
152 - const canvas = this.getCanvas();
153 -
154 - this._isSelected = false;
155 -
156 - canvas.defaultCursor = 'crosshair';
157 - canvas.selection = false;
158 - canvas.uniformScaling = true;
159 - canvas.on({
160 - 'mouse:down': this._handlers.mousedown,
161 - });
162 -
163 - fabric.util.addListener(document, 'keydown', this._handlers.keydown);
164 - fabric.util.addListener(document, 'keyup', this._handlers.keyup);
165 - }
166 -
167 - /**
168 - * End to draw the shape on canvas
169 - * @ignore
170 - */
171 - end() {
172 - const canvas = this.getCanvas();
173 -
174 - this._isSelected = false;
175 -
176 - canvas.defaultCursor = 'default';
177 -
178 - canvas.selection = true;
179 - canvas.uniformScaling = false;
180 - canvas.off({
181 - 'mouse:down': this._handlers.mousedown,
182 - });
183 -
184 - fabric.util.removeListener(document, 'keydown', this._handlers.keydown);
185 - fabric.util.removeListener(document, 'keyup', this._handlers.keyup);
186 - }
187 -
188 - /**
189 - * Set states of the current drawing shape
190 - * @ignore
191 - * @param {string} type - Shape type (ex: 'rect', 'circle')
192 - * @param {Object} [options] - Shape options
193 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
194 - * Shape foreground color (ex: '#fff', 'transparent')
195 - * @param {string} [options.stoke] - Shape outline color
196 - * @param {number} [options.strokeWidth] - Shape outline width
197 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
198 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
199 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
200 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
201 - */
202 - setStates(type, options) {
203 - this._type = type;
204 -
205 - if (options) {
206 - this._options = extend(this._options, options);
207 - }
208 - }
209 -
210 - /**
211 - * Add the shape
212 - * @ignore
213 - * @param {string} type - Shape type (ex: 'rect', 'circle')
214 - * @param {Object} options - Shape options
215 - * @param {(ShapeFillOption | string)} [options.fill] - ShapeFillOption or Shape foreground color (ex: '#fff', 'transparent') or ShapeFillOption object
216 - * @param {string} [options.stroke] - Shape outline color
217 - * @param {number} [options.strokeWidth] - Shape outline width
218 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
219 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
220 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
221 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
222 - * @param {number} [options.isRegular] - Whether scaling shape has 1:1 ratio or not
223 - * @returns {Promise}
224 - */
225 - add(type, options) {
226 - return new Promise((resolve) => {
227 - const canvas = this.getCanvas();
228 - const extendOption = this._extendOptions(options);
229 -
230 - const shapeObj = this._createInstance(type, extendOption);
231 - const objectProperties = this.graphics.createObjectProperties(shapeObj);
232 -
233 - this._bindEventOnShape(shapeObj);
234 -
235 - canvas.add(shapeObj).setActiveObject(shapeObj);
236 -
237 - this._resetPositionFillFilter(shapeObj);
238 -
239 - resolve(objectProperties);
240 - });
241 - }
242 -
243 - /**
244 - * Change the shape
245 - * @ignore
246 - * @param {fabric.Object} shapeObj - Selected shape object on canvas
247 - * @param {Object} options - Shape options
248 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
249 - * Shape foreground color (ex: '#fff', 'transparent')
250 - * @param {string} [options.stroke] - Shape outline color
251 - * @param {number} [options.strokeWidth] - Shape outline width
252 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
253 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
254 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
255 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
256 - * @param {number} [options.isRegular] - Whether scaling shape has 1:1 ratio or not
257 - * @returns {Promise}
258 - */
259 - change(shapeObj, options) {
260 - return new Promise((resolve, reject) => {
261 - if (!isShape(shapeObj)) {
262 - reject(rejectMessages.unsupportedType);
263 - }
264 - const hasFillOption = getFillTypeFromOption(options.fill) === 'filter';
265 - const { canvasImage, createStaticCanvas } = this.graphics;
266 -
267 - shapeObj.set(
268 - hasFillOption ? makeFabricFillOption(options, canvasImage, createStaticCanvas) : options
269 - );
270 -
271 - if (hasFillOption) {
272 - this._resetPositionFillFilter(shapeObj);
273 - }
274 -
275 - this.getCanvas().renderAll();
276 - resolve();
277 - });
278 - }
279 -
280 - /**
281 - * make fill property for user event
282 - * @param {fabric.Object} shapeObj - fabric object
283 - * @returns {Object}
284 - */
285 - makeFillPropertyForUserEvent(shapeObj) {
286 - const fillType = getFillTypeFromObject(shapeObj);
287 - const fillProp = {};
288 -
289 - if (fillType === SHAPE_FILL_TYPE.FILTER) {
290 - const fillImage = getFillImageFromShape(shapeObj);
291 - const filterOption = makeFilterOptionFromFabricImage(fillImage);
292 -
293 - fillProp.type = fillType;
294 - fillProp.filter = filterOption;
295 - } else {
296 - fillProp.type = SHAPE_FILL_TYPE.COLOR;
297 - fillProp.color = shapeObj.fill || 'transparent';
298 - }
299 -
300 - return fillProp;
301 - }
302 -
303 - /**
304 - * Copy object handling.
305 - * @param {fabric.Object} shapeObj - Shape object
306 - * @param {fabric.Object} originalShapeObj - Shape object
307 - */
308 - processForCopiedObject(shapeObj, originalShapeObj) {
309 - this._bindEventOnShape(shapeObj);
310 -
311 - if (getFillTypeFromObject(shapeObj) === 'filter') {
312 - const fillImage = getFillImageFromShape(originalShapeObj);
313 - const filterOption = makeFilterOptionFromFabricImage(fillImage);
314 - const newStaticCanvas = this.graphics.createStaticCanvas();
315 -
316 - shapeObj.set(
317 - makeFillPatternForFilter(this.graphics.canvasImage, filterOption, newStaticCanvas)
318 - );
319 - this._resetPositionFillFilter(shapeObj);
320 - }
321 - }
322 -
323 - /**
324 - * Create the instance of shape
325 - * @param {string} type - Shape type
326 - * @param {Object} options - Options to creat the shape
327 - * @returns {fabric.Object} Shape instance
328 - * @private
329 - */
330 - _createInstance(type, options) {
331 - let instance;
332 -
333 - switch (type) {
334 - case 'rect':
335 - instance = new fabric.Rect(options);
336 - break;
337 - case 'circle':
338 - instance = new fabric.Ellipse(
339 - extend(
340 - {
341 - type: 'circle',
342 - },
343 - options
344 - )
345 - );
346 - break;
347 - case 'triangle':
348 - instance = new fabric.Triangle(options);
349 - break;
350 - default:
351 - instance = {};
352 - }
353 -
354 - return instance;
355 - }
356 -
357 - /**
358 - * Get the options to create the shape
359 - * @param {Object} options - Options to creat the shape
360 - * @returns {Object} Shape options
361 - * @private
362 - */
363 - _extendOptions(options) {
364 - const selectionStyles = fObjectOptions.SELECTION_STYLE;
365 - const { canvasImage, createStaticCanvas } = this.graphics;
366 -
367 - options = extend({}, SHAPE_INIT_OPTIONS, this._options, selectionStyles, options);
368 -
369 - return makeFabricFillOption(options, canvasImage, createStaticCanvas);
370 - }
371 -
372 - /**
373 - * Bind fabric events on the creating shape object
374 - * @param {fabric.Object} shapeObj - Shape object
375 - * @private
376 - */
377 - _bindEventOnShape(shapeObj) {
378 - const self = this;
379 - const canvas = this.getCanvas();
380 -
381 - shapeObj.on({
382 - added() {
383 - self._shapeObj = this;
384 - resizeHelper.setOrigins(self._shapeObj);
385 - },
386 - selected() {
387 - self._isSelected = true;
388 - self._shapeObj = this;
389 - canvas.uniformScaling = true;
390 - canvas.defaultCursor = 'default';
391 - resizeHelper.setOrigins(self._shapeObj);
392 - },
393 - deselected() {
394 - self._isSelected = false;
395 - self._shapeObj = null;
396 - canvas.defaultCursor = 'crosshair';
397 - canvas.uniformScaling = false;
398 - },
399 - modified() {
400 - const currentObj = self._shapeObj;
401 -
402 - resizeHelper.adjustOriginToCenter(currentObj);
403 - resizeHelper.setOrigins(currentObj);
404 - },
405 - modifiedInGroup(activeSelection) {
406 - self._fillFilterRePositionInGroupSelection(shapeObj, activeSelection);
407 - },
408 - moving() {
409 - self._resetPositionFillFilter(this);
410 - },
411 - rotating() {
412 - self._resetPositionFillFilter(this);
413 - },
414 - scaling(fEvent) {
415 - const pointer = canvas.getPointer(fEvent.e);
416 - const currentObj = self._shapeObj;
417 -
418 - canvas.setCursor('crosshair');
419 - resizeHelper.resize(currentObj, pointer, true);
420 -
421 - self._resetPositionFillFilter(this);
422 - },
423 - });
424 - }
425 -
426 - /**
427 - * MouseDown event handler on canvas
428 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
429 - * @private
430 - */
431 - _onFabricMouseDown(fEvent) {
432 - if (!fEvent.target) {
433 - this._isSelected = false;
434 - this._shapeObj = false;
435 - }
436 -
437 - if (!this._isSelected && !this._shapeObj) {
438 - const canvas = this.getCanvas();
439 - this._startPoint = canvas.getPointer(fEvent.e);
440 -
441 - canvas.on({
442 - 'mouse:move': this._handlers.mousemove,
443 - 'mouse:up': this._handlers.mouseup,
444 - });
445 - }
446 - }
447 -
448 - /**
449 - * MouseDown event handler on canvas
450 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
451 - * @private
452 - */
453 - _onFabricMouseMove(fEvent) {
454 - const canvas = this.getCanvas();
455 - const pointer = canvas.getPointer(fEvent.e);
456 - const startPointX = this._startPoint.x;
457 - const startPointY = this._startPoint.y;
458 - const width = startPointX - pointer.x;
459 - const height = startPointY - pointer.y;
460 - const shape = this._shapeObj;
461 -
462 - if (!shape) {
463 - this.add(this._type, {
464 - left: startPointX,
465 - top: startPointY,
466 - width,
467 - height,
468 - }).then((objectProps) => {
469 - this.fire(eventNames.ADD_OBJECT, objectProps);
470 - });
471 - } else {
472 - this._shapeObj.set({
473 - isRegular: this._withShiftKey,
474 - });
475 -
476 - resizeHelper.resize(shape, pointer);
477 - canvas.renderAll();
478 -
479 - this._resetPositionFillFilter(shape);
480 - }
481 - }
482 -
483 - /**
484 - * MouseUp event handler on canvas
485 - * @private
486 - */
487 - _onFabricMouseUp() {
488 - const canvas = this.getCanvas();
489 - const startPointX = this._startPoint.x;
490 - const startPointY = this._startPoint.y;
491 - const shape = this._shapeObj;
492 -
493 - if (!shape) {
494 - this.add(this._type, {
495 - left: startPointX,
496 - top: startPointY,
497 - width: DEFAULT_WIDTH,
498 - height: DEFAULT_HEIGHT,
499 - }).then((objectProps) => {
500 - this.fire(eventNames.ADD_OBJECT, objectProps);
501 - });
502 - } else if (shape) {
503 - resizeHelper.adjustOriginToCenter(shape);
504 - this.fire(eventNames.OBJECT_ADDED, this.graphics.createObjectProperties(shape));
505 - }
506 -
507 - canvas.off({
508 - 'mouse:move': this._handlers.mousemove,
509 - 'mouse:up': this._handlers.mouseup,
510 - });
511 - }
512 -
513 - /**
514 - * Keydown event handler on document
515 - * @param {KeyboardEvent} e - Event object
516 - * @private
517 - */
518 - _onKeyDown(e) {
519 - if (e.keyCode === KEY_CODES.SHIFT) {
520 - this._withShiftKey = true;
521 -
522 - if (this._shapeObj) {
523 - this._shapeObj.isRegular = true;
524 - }
525 - }
526 - }
527 -
528 - /**
529 - * Keyup event handler on document
530 - * @param {KeyboardEvent} e - Event object
531 - * @private
532 - */
533 - _onKeyUp(e) {
534 - if (e.keyCode === KEY_CODES.SHIFT) {
535 - this._withShiftKey = false;
536 -
537 - if (this._shapeObj) {
538 - this._shapeObj.isRegular = false;
539 - }
540 - }
541 - }
542 -
543 - /**
544 - * Reset shape position and internal proportions in the filter type fill area.
545 - * @param {fabric.Object} shapeObj - Shape object
546 - * @private
547 - */
548 - _resetPositionFillFilter(shapeObj) {
549 - if (getFillTypeFromObject(shapeObj) !== 'filter') {
550 - return;
551 - }
552 -
553 - const { patternSourceCanvas } = getCustomProperty(shapeObj, 'patternSourceCanvas');
554 -
555 - const fillImage = getFillImageFromShape(shapeObj);
556 - const { originalAngle } = getCustomProperty(fillImage, 'originalAngle');
557 -
558 - if (this.graphics.canvasImage.angle !== originalAngle) {
559 - reMakePatternImageSource(shapeObj, this.graphics.canvasImage);
560 - }
561 - const { originX, originY } = shapeObj;
562 -
563 - resizeHelper.adjustOriginToCenter(shapeObj);
564 -
565 - shapeObj.width *= shapeObj.scaleX;
566 - shapeObj.height *= shapeObj.scaleY;
567 - shapeObj.rx *= shapeObj.scaleX;
568 - shapeObj.ry *= shapeObj.scaleY;
569 - shapeObj.scaleX = 1;
570 - shapeObj.scaleY = 1;
571 -
572 - rePositionFilterTypeFillImage(shapeObj);
573 -
574 - changeOrigin(shapeObj, {
575 - originX,
576 - originY,
577 - });
578 -
579 - resetFillPatternCanvas(patternSourceCanvas);
580 - }
581 -
582 - /**
583 - * Reset filter area position within group selection.
584 - * @param {fabric.Object} shapeObj - Shape object
585 - * @param {fabric.ActiveSelection} activeSelection - Shape object
586 - * @private
587 - */
588 - _fillFilterRePositionInGroupSelection(shapeObj, activeSelection) {
589 - if (activeSelection.scaleX !== 1 || activeSelection.scaleY !== 1) {
590 - // This is necessary because the group's scale transition state affects the relative size of the fill area.
591 - // The only way to reset the object transformation scale state to neutral.
592 - // {@link https://github.com/fabricjs/fabric.js/issues/5372}
593 - activeSelection.addWithUpdate();
594 - }
595 -
596 - const { angle, left, top } = shapeObj;
597 -
598 - activeSelection.realizeTransform(shapeObj);
599 - this._resetPositionFillFilter(shapeObj);
600 -
601 - shapeObj.set({
602 - angle,
603 - left,
604 - top,
605 - });
606 - }
607 -}
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Text module
4 - */
5 -import fabric from 'fabric';
6 -import snippet from 'tui-code-snippet';
7 -import Component from '../interface/component';
8 -import { eventNames as events, componentNames, fObjectOptions } from '../consts';
9 -import { Promise } from '../util';
10 -
11 -const defaultStyles = {
12 - fill: '#000000',
13 - left: 0,
14 - top: 0,
15 -};
16 -const resetStyles = {
17 - fill: '#000000',
18 - fontStyle: 'normal',
19 - fontWeight: 'normal',
20 - textAlign: 'left',
21 - underline: false,
22 -};
23 -
24 -const DBCLICK_TIME = 500;
25 -
26 -/**
27 - * Text
28 - * @class Text
29 - * @param {Graphics} graphics - Graphics instance
30 - * @extends {Component}
31 - * @ignore
32 - */
33 -class Text extends Component {
34 - constructor(graphics) {
35 - super(componentNames.TEXT, graphics);
36 -
37 - /**
38 - * Default text style
39 - * @type {Object}
40 - */
41 - this._defaultStyles = defaultStyles;
42 -
43 - /**
44 - * Selected state
45 - * @type {boolean}
46 - */
47 - this._isSelected = false;
48 -
49 - /**
50 - * Selected text object
51 - * @type {Object}
52 - */
53 - this._selectedObj = {};
54 -
55 - /**
56 - * Editing text object
57 - * @type {Object}
58 - */
59 - this._editingObj = {};
60 -
61 - /**
62 - * Listeners for fabric event
63 - * @type {Object}
64 - */
65 - this._listeners = {
66 - mousedown: this._onFabricMouseDown.bind(this),
67 - select: this._onFabricSelect.bind(this),
68 - selectClear: this._onFabricSelectClear.bind(this),
69 - scaling: this._onFabricScaling.bind(this),
70 - };
71 -
72 - /**
73 - * Textarea element for editing
74 - * @type {HTMLElement}
75 - */
76 - this._textarea = null;
77 -
78 - /**
79 - * Ratio of current canvas
80 - * @type {number}
81 - */
82 - this._ratio = 1;
83 -
84 - /**
85 - * Last click time
86 - * @type {Date}
87 - */
88 - this._lastClickTime = new Date().getTime();
89 -
90 - /**
91 - * Text object infos before editing
92 - * @type {Object}
93 - */
94 - this._editingObjInfos = {};
95 -
96 - /**
97 - * Previous state of editing
98 - * @type {boolean}
99 - */
100 - this.isPrevEditing = false;
101 - }
102 -
103 - /**
104 - * Start input text mode
105 - */
106 - start() {
107 - const canvas = this.getCanvas();
108 -
109 - canvas.selection = false;
110 - canvas.defaultCursor = 'text';
111 - canvas.on({
112 - 'mouse:down': this._listeners.mousedown,
113 - 'selection:created': this._listeners.select,
114 - 'selection:updated': this._listeners.select,
115 - 'before:selection:cleared': this._listeners.selectClear,
116 - 'object:scaling': this._listeners.scaling,
117 - 'text:editing': this._listeners.modify,
118 - });
119 -
120 - canvas.forEachObject((obj) => {
121 - if (obj.type === 'i-text') {
122 - this.adjustOriginPosition(obj, 'start');
123 - }
124 - });
125 -
126 - this.setCanvasRatio();
127 - }
128 -
129 - /**
130 - * End input text mode
131 - */
132 - end() {
133 - const canvas = this.getCanvas();
134 -
135 - canvas.selection = true;
136 - canvas.defaultCursor = 'default';
137 -
138 - canvas.forEachObject((obj) => {
139 - if (obj.type === 'i-text') {
140 - if (obj.text === '') {
141 - canvas.remove(obj);
142 - } else {
143 - this.adjustOriginPosition(obj, 'end');
144 - }
145 - }
146 - });
147 -
148 - canvas.off({
149 - 'mouse:down': this._listeners.mousedown,
150 - 'object:selected': this._listeners.select,
151 - 'before:selection:cleared': this._listeners.selectClear,
152 - 'object:scaling': this._listeners.scaling,
153 - 'text:editing': this._listeners.modify,
154 - });
155 - }
156 -
157 - /**
158 - * Adjust the origin position
159 - * @param {fabric.Object} text - text object
160 - * @param {string} editStatus - 'start' or 'end'
161 - */
162 - adjustOriginPosition(text, editStatus) {
163 - let [originX, originY] = ['center', 'center'];
164 - if (editStatus === 'start') {
165 - [originX, originY] = ['left', 'top'];
166 - }
167 -
168 - const { x: left, y: top } = text.getPointByOrigin(originX, originY);
169 - text.set({
170 - left,
171 - top,
172 - originX,
173 - originY,
174 - });
175 - text.setCoords();
176 - }
177 -
178 - /**
179 - * Add new text on canvas image
180 - * @param {string} text - Initial input text
181 - * @param {Object} options - Options for generating text
182 - * @param {Object} [options.styles] Initial styles
183 - * @param {string} [options.styles.fill] Color
184 - * @param {string} [options.styles.fontFamily] Font type for text
185 - * @param {number} [options.styles.fontSize] Size
186 - * @param {string} [options.styles.fontStyle] Type of inclination (normal / italic)
187 - * @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold)
188 - * @param {string} [options.styles.textAlign] Type of text align (left / center / right)
189 - * @param {string} [options.styles.textDecoration] Type of line (underline / line-through / overline)
190 - * @param {{x: number, y: number}} [options.position] - Initial position
191 - * @returns {Promise}
192 - */
193 - add(text, options) {
194 - return new Promise((resolve) => {
195 - const canvas = this.getCanvas();
196 - let newText = null;
197 - let selectionStyle = fObjectOptions.SELECTION_STYLE;
198 - let styles = this._defaultStyles;
199 -
200 - this._setInitPos(options.position);
201 -
202 - if (options.styles) {
203 - styles = snippet.extend(styles, options.styles);
204 - }
205 -
206 - if (!snippet.isExisty(options.autofocus)) {
207 - options.autofocus = true;
208 - }
209 -
210 - newText = new fabric.IText(text, styles);
211 - selectionStyle = snippet.extend({}, selectionStyle, {
212 - originX: 'left',
213 - originY: 'top',
214 - });
215 -
216 - newText.set(selectionStyle);
217 - newText.on({
218 - mouseup: this._onFabricMouseUp.bind(this),
219 - });
220 -
221 - canvas.add(newText);
222 -
223 - if (options.autofocus) {
224 - newText.enterEditing();
225 - newText.selectAll();
226 - }
227 -
228 - if (!canvas.getActiveObject()) {
229 - canvas.setActiveObject(newText);
230 - }
231 -
232 - this.isPrevEditing = true;
233 - resolve(this.graphics.createObjectProperties(newText));
234 - });
235 - }
236 -
237 - /**
238 - * Change text of activate object on canvas image
239 - * @param {Object} activeObj - Current selected text object
240 - * @param {string} text - Changed text
241 - * @returns {Promise}
242 - */
243 - change(activeObj, text) {
244 - return new Promise((resolve) => {
245 - activeObj.set('text', text);
246 -
247 - this.getCanvas().renderAll();
248 - resolve();
249 - });
250 - }
251 -
252 - /**
253 - * Set style
254 - * @param {Object} activeObj - Current selected text object
255 - * @param {Object} styleObj - Initial styles
256 - * @param {string} [styleObj.fill] Color
257 - * @param {string} [styleObj.fontFamily] Font type for text
258 - * @param {number} [styleObj.fontSize] Size
259 - * @param {string} [styleObj.fontStyle] Type of inclination (normal / italic)
260 - * @param {string} [styleObj.fontWeight] Type of thicker or thinner looking (normal / bold)
261 - * @param {string} [styleObj.textAlign] Type of text align (left / center / right)
262 - * @param {string} [styleObj.textDecoration] Type of line (underline / line-through / overline)
263 - * @returns {Promise}
264 - */
265 - setStyle(activeObj, styleObj) {
266 - return new Promise((resolve) => {
267 - snippet.forEach(
268 - styleObj,
269 - (val, key) => {
270 - if (activeObj[key] === val && key !== 'fontSize') {
271 - styleObj[key] = resetStyles[key] || '';
272 - }
273 - },
274 - this
275 - );
276 -
277 - if ('textDecoration' in styleObj) {
278 - snippet.extend(styleObj, this._getTextDecorationAdaptObject(styleObj.textDecoration));
279 - }
280 -
281 - activeObj.set(styleObj);
282 -
283 - this.getCanvas().renderAll();
284 - resolve();
285 - });
286 - }
287 -
288 - /**
289 - * Get the text
290 - * @param {Object} activeObj - Current selected text object
291 - * @returns {String} text
292 - */
293 - getText(activeObj) {
294 - return activeObj.text;
295 - }
296 -
297 - /**
298 - * Set infos of the current selected object
299 - * @param {fabric.Text} obj - Current selected text object
300 - * @param {boolean} state - State of selecting
301 - */
302 - setSelectedInfo(obj, state) {
303 - this._selectedObj = obj;
304 - this._isSelected = state;
305 - }
306 -
307 - /**
308 - * Whether object is selected or not
309 - * @returns {boolean} State of selecting
310 - */
311 - isSelected() {
312 - return this._isSelected;
313 - }
314 -
315 - /**
316 - * Get current selected text object
317 - * @returns {fabric.Text} Current selected text object
318 - */
319 - getSelectedObj() {
320 - return this._selectedObj;
321 - }
322 -
323 - /**
324 - * Set ratio value of canvas
325 - */
326 - setCanvasRatio() {
327 - const canvasElement = this.getCanvasElement();
328 - const cssWidth = parseInt(canvasElement.style.maxWidth, 10);
329 - const originWidth = canvasElement.width;
330 - const ratio = originWidth / cssWidth;
331 -
332 - this._ratio = ratio;
333 - }
334 -
335 - /**
336 - * Get ratio value of canvas
337 - * @returns {number} Ratio value
338 - */
339 - getCanvasRatio() {
340 - return this._ratio;
341 - }
342 -
343 - /**
344 - * Get text decoration adapt object
345 - * @param {string} textDecoration - text decoration option string
346 - * @returns {object} adapt object for override
347 - */
348 - _getTextDecorationAdaptObject(textDecoration) {
349 - return {
350 - underline: textDecoration === 'underline',
351 - linethrough: textDecoration === 'line-through',
352 - overline: textDecoration === 'overline',
353 - };
354 - }
355 -
356 - /**
357 - * Set initial position on canvas image
358 - * @param {{x: number, y: number}} [position] - Selected position
359 - * @private
360 - */
361 - _setInitPos(position) {
362 - position = position || this.getCanvasImage().getCenterPoint();
363 -
364 - this._defaultStyles.left = position.x;
365 - this._defaultStyles.top = position.y;
366 - }
367 -
368 - /**
369 - * Input event handler
370 - * @private
371 - */
372 - _onInput() {
373 - const ratio = this.getCanvasRatio();
374 - const obj = this._editingObj;
375 - const textareaStyle = this._textarea.style;
376 -
377 - textareaStyle.width = `${Math.ceil(obj.width / ratio)}px`;
378 - textareaStyle.height = `${Math.ceil(obj.height / ratio)}px`;
379 - }
380 -
381 - /**
382 - * Keydown event handler
383 - * @private
384 - */
385 - _onKeyDown() {
386 - const ratio = this.getCanvasRatio();
387 - const obj = this._editingObj;
388 - const textareaStyle = this._textarea.style;
389 -
390 - setTimeout(() => {
391 - obj.text(this._textarea.value);
392 -
393 - textareaStyle.width = `${Math.ceil(obj.width / ratio)}px`;
394 - textareaStyle.height = `${Math.ceil(obj.height / ratio)}px`;
395 - }, 0);
396 - }
397 -
398 - /**
399 - * Blur event handler
400 - * @private
401 - */
402 - _onBlur() {
403 - const ratio = this.getCanvasRatio();
404 - const editingObj = this._editingObj;
405 - const editingObjInfos = this._editingObjInfos;
406 - const textContent = this._textarea.value;
407 - let transWidth = editingObj.width / ratio - editingObjInfos.width / ratio;
408 - let transHeight = editingObj.height / ratio - editingObjInfos.height / ratio;
409 -
410 - if (ratio === 1) {
411 - transWidth /= 2;
412 - transHeight /= 2;
413 - }
414 -
415 - this._textarea.style.display = 'none';
416 -
417 - editingObj.set({
418 - left: editingObjInfos.left + transWidth,
419 - top: editingObjInfos.top + transHeight,
420 - });
421 -
422 - if (textContent.length) {
423 - this.getCanvas().add(editingObj);
424 -
425 - const params = {
426 - id: snippet.stamp(editingObj),
427 - type: editingObj.type,
428 - text: textContent,
429 - };
430 -
431 - this.fire(events.TEXT_CHANGED, params);
432 - }
433 - }
434 -
435 - /**
436 - * Scroll event handler
437 - * @private
438 - */
439 - _onScroll() {
440 - this._textarea.scrollLeft = 0;
441 - this._textarea.scrollTop = 0;
442 - }
443 -
444 - /**
445 - * Fabric scaling event handler
446 - * @param {fabric.Event} fEvent - Current scaling event on selected object
447 - * @private
448 - */
449 - _onFabricScaling(fEvent) {
450 - const obj = fEvent.target;
451 - const scalingSize = obj.fontSize * obj.scaleY;
452 -
453 - obj.fontSize = scalingSize;
454 - obj.scaleX = 1;
455 - obj.scaleY = 1;
456 - }
457 -
458 - /**
459 - * onSelectClear handler in fabric canvas
460 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
461 - * @private
462 - */
463 - _onFabricSelectClear(fEvent) {
464 - const obj = this.getSelectedObj();
465 -
466 - this.isPrevEditing = true;
467 -
468 - this.setSelectedInfo(fEvent.target, false);
469 -
470 - if (obj) {
471 - // obj is empty object at initial time, will be set fabric object
472 - if (obj.text === '') {
473 - this.getCanvas().remove(obj);
474 - }
475 - }
476 - }
477 -
478 - /**
479 - * onSelect handler in fabric canvas
480 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
481 - * @private
482 - */
483 - _onFabricSelect(fEvent) {
484 - this.isPrevEditing = true;
485 -
486 - this.setSelectedInfo(fEvent.target, true);
487 - }
488 -
489 - /**
490 - * Fabric 'mousedown' event handler
491 - * @param {fabric.Event} fEvent - Current mousedown event on selected object
492 - * @private
493 - */
494 - _onFabricMouseDown(fEvent) {
495 - const obj = fEvent.target;
496 -
497 - if (obj && !obj.isType('text')) {
498 - return;
499 - }
500 -
501 - if (this.isPrevEditing) {
502 - this.isPrevEditing = false;
503 -
504 - return;
505 - }
506 -
507 - this._fireAddText(fEvent);
508 - }
509 -
510 - /**
511 - * Fire 'addText' event if object is not selected.
512 - * @param {fabric.Event} fEvent - Current mousedown event on selected object
513 - * @private
514 - */
515 - _fireAddText(fEvent) {
516 - const obj = fEvent.target;
517 - const e = fEvent.e || {};
518 - const originPointer = this.getCanvas().getPointer(e);
519 -
520 - if (!obj) {
521 - this.fire(events.ADD_TEXT, {
522 - originPosition: {
523 - x: originPointer.x,
524 - y: originPointer.y,
525 - },
526 - clientPosition: {
527 - x: e.clientX || 0,
528 - y: e.clientY || 0,
529 - },
530 - });
531 - }
532 - }
533 -
534 - /**
535 - * Fabric mouseup event handler
536 - * @param {fabric.Event} fEvent - Current mousedown event on selected object
537 - * @private
538 - */
539 - _onFabricMouseUp(fEvent) {
540 - const { target } = fEvent;
541 - const newClickTime = new Date().getTime();
542 -
543 - if (this._isDoubleClick(newClickTime) && !target.isEditing) {
544 - target.enterEditing();
545 - }
546 -
547 - if (target.isEditing) {
548 - this.fire(events.TEXT_EDITING); // fire editing text event
549 - }
550 -
551 - this._lastClickTime = newClickTime;
552 - }
553 -
554 - /**
555 - * Get state of firing double click event
556 - * @param {Date} newClickTime - Current clicked time
557 - * @returns {boolean} Whether double clicked or not
558 - * @private
559 - */
560 - _isDoubleClick(newClickTime) {
561 - return newClickTime - this._lastClickTime < DBCLICK_TIME;
562 - }
563 -}
564 -
565 -export default Text;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Constants
4 - */
5 -import { keyMirror } from './util';
6 -
7 -/**
8 - * Editor help features
9 - * @type {Array.<string>}
10 - */
11 -export const HELP_MENUS = ['undo', 'redo', 'reset', 'delete', 'deleteAll'];
12 -
13 -/**
14 - * Filter name value map
15 - * @type {Object.<string, string>}
16 - */
17 -export const FILTER_NAME_VALUE_MAP = {
18 - blur: 'blur',
19 - blocksize: 'pixelate',
20 -};
21 -
22 -/**
23 - * Fill type for shape
24 - * @type {Object.<string, string>}
25 - */
26 -export const SHAPE_FILL_TYPE = {
27 - FILTER: 'filter',
28 - COLOR: 'color',
29 -};
30 -
31 -/**
32 - * Shape type list
33 - * @type {Array.<string>}
34 - */
35 -export const SHAPE_TYPE = ['rect', 'circle', 'triangle'];
36 -
37 -/**
38 - * Component names
39 - * @type {Object.<string, string>}
40 - */
41 -export const componentNames = keyMirror(
42 - 'IMAGE_LOADER',
43 - 'CROPPER',
44 - 'FLIP',
45 - 'ROTATION',
46 - 'FREE_DRAWING',
47 - 'LINE',
48 - 'TEXT',
49 - 'ICON',
50 - 'FILTER',
51 - 'SHAPE'
52 -);
53 -
54 -/**
55 - * Shape default option
56 - * @type {Object}
57 - */
58 -export const SHAPE_DEFAULT_OPTIONS = {
59 - lockSkewingX: true,
60 - lockSkewingY: true,
61 - bringForward: true,
62 - isRegular: false,
63 -};
64 -
65 -/**
66 - * Cropzone default option
67 - * @type {Object}
68 - */
69 -export const CROPZONE_DEFAULT_OPTIONS = {
70 - hasRotatingPoint: false,
71 - hasBorders: false,
72 - lockScalingFlip: true,
73 - lockRotation: true,
74 - lockSkewingX: true,
75 - lockSkewingY: true,
76 -};
77 -
78 -/**
79 - * Command names
80 - * @type {Object.<string, string>}
81 - */
82 -export const commandNames = {
83 - CLEAR_OBJECTS: 'clearObjects',
84 - LOAD_IMAGE: 'loadImage',
85 - FLIP_IMAGE: 'flip',
86 - ROTATE_IMAGE: 'rotate',
87 - ADD_OBJECT: 'addObject',
88 - REMOVE_OBJECT: 'removeObject',
89 - APPLY_FILTER: 'applyFilter',
90 - REMOVE_FILTER: 'removeFilter',
91 - ADD_ICON: 'addIcon',
92 - CHANGE_ICON_COLOR: 'changeIconColor',
93 - ADD_SHAPE: 'addShape',
94 - CHANGE_SHAPE: 'changeShape',
95 - ADD_TEXT: 'addText',
96 - CHANGE_TEXT: 'changeText',
97 - CHANGE_TEXT_STYLE: 'changeTextStyle',
98 - ADD_IMAGE_OBJECT: 'addImageObject',
99 - RESIZE_CANVAS_DIMENSION: 'resizeCanvasDimension',
100 - SET_OBJECT_PROPERTIES: 'setObjectProperties',
101 - SET_OBJECT_POSITION: 'setObjectPosition',
102 - CHANGE_SELECTION: 'changeSelection',
103 -};
104 -
105 -/**
106 - * Event names
107 - * @type {Object.<string, string>}
108 - */
109 -export const eventNames = {
110 - OBJECT_ACTIVATED: 'objectActivated',
111 - OBJECT_MOVED: 'objectMoved',
112 - OBJECT_SCALED: 'objectScaled',
113 - OBJECT_CREATED: 'objectCreated',
114 - OBJECT_ROTATED: 'objectRotated',
115 - OBJECT_ADDED: 'objectAdded',
116 - OBJECT_MODIFIED: 'objectModified',
117 - TEXT_EDITING: 'textEditing',
118 - TEXT_CHANGED: 'textChanged',
119 - ICON_CREATE_RESIZE: 'iconCreateResize',
120 - ICON_CREATE_END: 'iconCreateEnd',
121 - ADD_TEXT: 'addText',
122 - ADD_OBJECT: 'addObject',
123 - ADD_OBJECT_AFTER: 'addObjectAfter',
124 - MOUSE_DOWN: 'mousedown',
125 - MOUSE_UP: 'mouseup',
126 - MOUSE_MOVE: 'mousemove',
127 - // UNDO/REDO Events
128 - REDO_STACK_CHANGED: 'redoStackChanged',
129 - UNDO_STACK_CHANGED: 'undoStackChanged',
130 - SELECTION_CLEARED: 'selectionCleared',
131 - SELECTION_CREATED: 'selectionCreated',
132 -};
133 -
134 -/**
135 - * Editor states
136 - * @type {Object.<string, string>}
137 - */
138 -export const drawingModes = keyMirror(
139 - 'NORMAL',
140 - 'CROPPER',
141 - 'FREE_DRAWING',
142 - 'LINE_DRAWING',
143 - 'TEXT',
144 - 'SHAPE',
145 - 'ICON'
146 -);
147 -
148 -/**
149 - * Shortcut key values
150 - * @type {Object.<string, number>}
151 - */
152 -export const keyCodes = {
153 - Z: 90,
154 - Y: 89,
155 - C: 67,
156 - V: 86,
157 - SHIFT: 16,
158 - BACKSPACE: 8,
159 - DEL: 46,
160 - ARROW_DOWN: 40,
161 - ARROW_UP: 38,
162 -};
163 -
164 -/**
165 - * Fabric object options
166 - * @type {Object.<string, Object>}
167 - */
168 -export const fObjectOptions = {
169 - SELECTION_STYLE: {
170 - borderColor: 'red',
171 - cornerColor: 'green',
172 - cornerSize: 10,
173 - originX: 'center',
174 - originY: 'center',
175 - transparentCorners: false,
176 - },
177 -};
178 -
179 -/**
180 - * Promise reject messages
181 - * @type {Object.<string, string>}
182 - */
183 -export const rejectMessages = {
184 - addedObject: 'The object is already added.',
185 - flip: 'The flipX and flipY setting values are not changed.',
186 - invalidDrawingMode: 'This operation is not supported in the drawing mode.',
187 - invalidParameters: 'Invalid parameters.',
188 - isLock: 'The executing command state is locked.',
189 - loadImage: 'The background image is empty.',
190 - loadingImageFailed: 'Invalid image loaded.',
191 - noActiveObject: 'There is no active object.',
192 - noObject: 'The object is not in canvas.',
193 - redo: 'The promise of redo command is reject.',
194 - rotation: 'The current angle is same the old angle.',
195 - undo: 'The promise of undo command is reject.',
196 - unsupportedOperation: 'Unsupported operation.',
197 - unsupportedType: 'Unsupported object type.',
198 -};
199 -
200 -/**
201 - * Default icon menu svg path
202 - * @type {Object.<string, string>}
203 - */
204 -export const defaultIconPath = {
205 - 'icon-arrow': 'M40 12V0l24 24-24 24V36H0V12h40z',
206 - 'icon-arrow-2': 'M49,32 H3 V22 h46 l-18,-18 h12 l23,23 L43,50 h-12 l18,-18 z ',
207 - 'icon-arrow-3':
208 - 'M43.349998,27 L17.354,53 H1.949999 l25.996,-26 L1.949999,1 h15.404 L43.349998,27 z ',
209 - 'icon-star':
210 - 'M35,54.557999 l-19.912001,10.468 l3.804,-22.172001 l-16.108,-15.7 l22.26,-3.236 L35,3.746 l9.956,20.172001 l22.26,3.236 l-16.108,15.7 l3.804,22.172001 z ',
211 - 'icon-star-2':
212 - 'M17,31.212 l-7.194,4.08 l-4.728,-6.83 l-8.234,0.524 l-1.328,-8.226 l-7.644,-3.14 l2.338,-7.992 l-5.54,-6.18 l5.54,-6.176 l-2.338,-7.994 l7.644,-3.138 l1.328,-8.226 l8.234,0.522 l4.728,-6.83 L17,-24.312 l7.194,-4.08 l4.728,6.83 l8.234,-0.522 l1.328,8.226 l7.644,3.14 l-2.338,7.992 l5.54,6.178 l-5.54,6.178 l2.338,7.992 l-7.644,3.14 l-1.328,8.226 l-8.234,-0.524 l-4.728,6.83 z ',
213 - 'icon-polygon': 'M3,31 L19,3 h32 l16,28 l-16,28 H19 z ',
214 - 'icon-location':
215 - 'M24 62C8 45.503 0 32.837 0 24 0 10.745 10.745 0 24 0s24 10.745 24 24c0 8.837-8 21.503-24 38zm0-28c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10z',
216 - 'icon-heart':
217 - 'M49.994999,91.349998 l-6.96,-6.333 C18.324001,62.606995 2.01,47.829002 2.01,29.690998 C2.01,14.912998 13.619999,3.299999 28.401001,3.299999 c8.349,0 16.362,5.859 21.594,12 c5.229,-6.141 13.242001,-12 21.591,-12 c14.778,0 26.390999,11.61 26.390999,26.390999 c0,18.138 -16.314001,32.916 -41.025002,55.374001 l-6.96,6.285 z ',
218 - 'icon-bubble':
219 - 'M44 48L34 58V48H12C5.373 48 0 42.627 0 36V12C0 5.373 5.373 0 12 0h40c6.627 0 12 5.373 12 12v24c0 6.627-5.373 12-12 12h-8z',
220 -};
221 -
222 -export const defaultRotateRangeValus = {
223 - realTimeEvent: true,
224 - min: -360,
225 - max: 360,
226 - value: 0,
227 -};
228 -
229 -export const defaultDrawRangeValus = {
230 - min: 5,
231 - max: 30,
232 - value: 12,
233 -};
234 -
235 -export const defaultShapeStrokeValus = {
236 - realTimeEvent: true,
237 - min: 2,
238 - max: 300,
239 - value: 3,
240 -};
241 -
242 -export const defaultTextRangeValus = {
243 - realTimeEvent: true,
244 - min: 10,
245 - max: 100,
246 - value: 50,
247 -};
248 -
249 -export const defaultFilterRangeValus = {
250 - tintOpacityRange: {
251 - realTimeEvent: true,
252 - min: 0,
253 - max: 1,
254 - value: 0.7,
255 - useDecimal: true,
256 - },
257 - removewhiteDistanceRange: {
258 - realTimeEvent: true,
259 - min: 0,
260 - max: 1,
261 - value: 0.2,
262 - useDecimal: true,
263 - },
264 - brightnessRange: {
265 - realTimeEvent: true,
266 - min: -1,
267 - max: 1,
268 - value: 0,
269 - useDecimal: true,
270 - },
271 - noiseRange: {
272 - realTimeEvent: true,
273 - min: 0,
274 - max: 1000,
275 - value: 100,
276 - },
277 - pixelateRange: {
278 - realTimeEvent: true,
279 - min: 2,
280 - max: 20,
281 - value: 4,
282 - },
283 - colorfilterThresholeRange: {
284 - realTimeEvent: true,
285 - min: 0,
286 - max: 1,
287 - value: 0.2,
288 - useDecimal: true,
289 - },
290 - blurFilterRange: {
291 - value: 0.1,
292 - },
293 -};
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview CropperDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * CropperDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class CropperDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.CROPPER);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @override
22 - */
23 - start(graphics) {
24 - const cropper = graphics.getComponent(components.CROPPER);
25 - cropper.start();
26 - }
27 -
28 - /**
29 - * stop this drawing mode
30 - * @param {Graphics} graphics - Graphics instance
31 - * @override
32 - */
33 - end(graphics) {
34 - const cropper = graphics.getComponent(components.CROPPER);
35 - cropper.end();
36 - }
37 -}
38 -
39 -export default CropperDrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview FreeDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * FreeDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class FreeDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.FREE_DRAWING);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @param {{width: ?number, color: ?string}} [options] - Brush width & color
22 - * @override
23 - */
24 - start(graphics, options) {
25 - const freeDrawing = graphics.getComponent(components.FREE_DRAWING);
26 - freeDrawing.start(options);
27 - }
28 -
29 - /**
30 - * stop this drawing mode
31 - * @param {Graphics} graphics - Graphics instance
32 - * @override
33 - */
34 - end(graphics) {
35 - const freeDrawing = graphics.getComponent(components.FREE_DRAWING);
36 - freeDrawing.end();
37 - }
38 -}
39 -
40 -export default FreeDrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview IconDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * IconDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class IconDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.ICON);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @override
22 - */
23 - start(graphics) {
24 - const icon = graphics.getComponent(components.ICON);
25 - icon.start();
26 - }
27 -
28 - /**
29 - * stop this drawing mode
30 - * @param {Graphics} graphics - Graphics instance
31 - * @override
32 - */
33 - end(graphics) {
34 - const icon = graphics.getComponent(components.ICON);
35 - icon.end();
36 - }
37 -}
38 -
39 -export default IconDrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview LineDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * LineDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class LineDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.LINE_DRAWING);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @param {{width: ?number, color: ?string}} [options] - Brush width & color
22 - * @override
23 - */
24 - start(graphics, options) {
25 - const lineDrawing = graphics.getComponent(components.LINE);
26 - lineDrawing.start(options);
27 - }
28 -
29 - /**
30 - * stop this drawing mode
31 - * @param {Graphics} graphics - Graphics instance
32 - * @override
33 - */
34 - end(graphics) {
35 - const lineDrawing = graphics.getComponent(components.LINE);
36 - lineDrawing.end();
37 - }
38 -}
39 -
40 -export default LineDrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview ShapeDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * ShapeDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class ShapeDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.SHAPE);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @override
22 - */
23 - start(graphics) {
24 - const shape = graphics.getComponent(components.SHAPE);
25 - shape.start();
26 - }
27 -
28 - /**
29 - * stop this drawing mode
30 - * @param {Graphics} graphics - Graphics instance
31 - * @override
32 - */
33 - end(graphics) {
34 - const shape = graphics.getComponent(components.SHAPE);
35 - shape.end();
36 - }
37 -}
38 -
39 -export default ShapeDrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview TextDrawingMode class
4 - */
5 -import DrawingMode from '../interface/drawingMode';
6 -import { drawingModes, componentNames as components } from '../consts';
7 -
8 -/**
9 - * TextDrawingMode class
10 - * @class
11 - * @ignore
12 - */
13 -class TextDrawingMode extends DrawingMode {
14 - constructor() {
15 - super(drawingModes.TEXT);
16 - }
17 -
18 - /**
19 - * start this drawing mode
20 - * @param {Graphics} graphics - Graphics instance
21 - * @override
22 - */
23 - start(graphics) {
24 - const text = graphics.getComponent(components.TEXT);
25 - text.start();
26 - }
27 -
28 - /**
29 - * stop this drawing mode
30 - * @param {Graphics} graphics - Graphics instance
31 - * @override
32 - */
33 - end(graphics) {
34 - const text = graphics.getComponent(components.TEXT);
35 - text.end();
36 - }
37 -}
38 -
39 -export default TextDrawingMode;
1 -/**
2 - * @author NHN. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Blur extending fabric.Image.filters.Convolute
4 - */
5 -import fabric from 'fabric';
6 -
7 -const ARROW_ANGLE = 30;
8 -const CHEVRON_SIZE_RATIO = 2.7;
9 -const TRIANGLE_SIZE_RATIO = 1.7;
10 -const RADIAN_CONVERSION_VALUE = 180;
11 -
12 -const ArrowLine = fabric.util.createClass(
13 - fabric.Line,
14 - /** @lends Convolute.prototype */ {
15 - /**
16 - * Line type
17 - * @param {String} type
18 - * @default
19 - */
20 - type: 'line',
21 -
22 - /**
23 - * Constructor
24 - * @param {Array} [points] Array of points
25 - * @param {Object} [options] Options object
26 - * @override
27 - */
28 - initialize(points, options = {}) {
29 - this.callSuper('initialize', points, options);
30 -
31 - this.arrowType = options.arrowType;
32 - },
33 -
34 - /**
35 - * Render ArrowLine
36 - * @private
37 - * @override
38 - */
39 - _render(ctx) {
40 - const { x1: fromX, y1: fromY, x2: toX, y2: toY } = this.calcLinePoints();
41 - const linePosition = {
42 - fromX,
43 - fromY,
44 - toX,
45 - toY,
46 - };
47 - this.ctx = ctx;
48 - ctx.lineWidth = this.strokeWidth;
49 -
50 - this._renderBasicLinePath(linePosition);
51 - this._drawDecoratorPath(linePosition);
52 -
53 - this._renderStroke(ctx);
54 - },
55 -
56 - /**
57 - * Render Basic line path
58 - * @param {Object} linePosition - line position
59 - * @param {number} option.fromX - line start position x
60 - * @param {number} option.fromY - line start position y
61 - * @param {number} option.toX - line end position x
62 - * @param {number} option.toY - line end position y
63 - * @private
64 - */
65 - _renderBasicLinePath({ fromX, fromY, toX, toY }) {
66 - this.ctx.beginPath();
67 - this.ctx.moveTo(fromX, fromY);
68 - this.ctx.lineTo(toX, toY);
69 - },
70 -
71 - /**
72 - * Render Arrow Head
73 - * @param {Object} linePosition - line position
74 - * @param {number} option.fromX - line start position x
75 - * @param {number} option.fromY - line start position y
76 - * @param {number} option.toX - line end position x
77 - * @param {number} option.toY - line end position y
78 - * @private
79 - */
80 - _drawDecoratorPath(linePosition) {
81 - this._drawDecoratorPathType('head', linePosition);
82 - this._drawDecoratorPathType('tail', linePosition);
83 - },
84 -
85 - /**
86 - * Render Arrow Head
87 - * @param {string} type - 'head' or 'tail'
88 - * @param {Object} linePosition - line position
89 - * @param {number} option.fromX - line start position x
90 - * @param {number} option.fromY - line start position y
91 - * @param {number} option.toX - line end position x
92 - * @param {number} option.toY - line end position y
93 - * @private
94 - */
95 - _drawDecoratorPathType(type, linePosition) {
96 - switch (this.arrowType[type]) {
97 - case 'triangle':
98 - this._drawTrianglePath(type, linePosition);
99 - break;
100 - case 'chevron':
101 - this._drawChevronPath(type, linePosition);
102 - break;
103 - default:
104 - break;
105 - }
106 - },
107 -
108 - /**
109 - * Render Triangle Head
110 - * @param {string} type - 'head' or 'tail'
111 - * @param {Object} linePosition - line position
112 - * @param {number} option.fromX - line start position x
113 - * @param {number} option.fromY - line start position y
114 - * @param {number} option.toX - line end position x
115 - * @param {number} option.toY - line end position y
116 - * @private
117 - */
118 - _drawTrianglePath(type, linePosition) {
119 - const decorateSize = this.ctx.lineWidth * TRIANGLE_SIZE_RATIO;
120 -
121 - this._drawChevronPath(type, linePosition, decorateSize);
122 - this.ctx.closePath();
123 - },
124 -
125 - /**
126 - * Render Chevron Head
127 - * @param {string} type - 'head' or 'tail'
128 - * @param {Object} linePosition - line position
129 - * @param {number} option.fromX - line start position x
130 - * @param {number} option.fromY - line start position y
131 - * @param {number} option.toX - line end position x
132 - * @param {number} option.toY - line end position y
133 - * @param {number} decorateSize - decorate size
134 - * @private
135 - */
136 - _drawChevronPath(type, { fromX, fromY, toX, toY }, decorateSize) {
137 - const { ctx } = this;
138 - if (!decorateSize) {
139 - decorateSize = this.ctx.lineWidth * CHEVRON_SIZE_RATIO;
140 - }
141 -
142 - const [standardX, standardY] = type === 'head' ? [fromX, fromY] : [toX, toY];
143 - const [compareX, compareY] = type === 'head' ? [toX, toY] : [fromX, fromY];
144 -
145 - const angle =
146 - (Math.atan2(compareY - standardY, compareX - standardX) * RADIAN_CONVERSION_VALUE) /
147 - Math.PI;
148 - const rotatedPosition = (changeAngle) =>
149 - this.getRotatePosition(decorateSize, changeAngle, {
150 - x: standardX,
151 - y: standardY,
152 - });
153 -
154 - ctx.moveTo(...rotatedPosition(angle + ARROW_ANGLE));
155 - ctx.lineTo(standardX, standardY);
156 - ctx.lineTo(...rotatedPosition(angle - ARROW_ANGLE));
157 - },
158 -
159 - /**
160 - * return position from change angle.
161 - * @param {number} distance - change distance
162 - * @param {number} angle - change angle
163 - * @param {Object} referencePosition - reference position
164 - * @returns {Array}
165 - * @private
166 - */
167 - getRotatePosition(distance, angle, referencePosition) {
168 - const radian = (angle * Math.PI) / RADIAN_CONVERSION_VALUE;
169 - const { x, y } = referencePosition;
170 -
171 - return [distance * Math.cos(radian) + x, distance * Math.sin(radian) + y];
172 - },
173 - }
174 -);
175 -
176 -export default ArrowLine;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Blur extending fabric.Image.filters.Convolute
4 - */
5 -import fabric from 'fabric';
6 -
7 -/**
8 - * Blur object
9 - * @class Blur
10 - * @extends {fabric.Image.filters.Convolute}
11 - * @ignore
12 - */
13 -const Blur = fabric.util.createClass(
14 - fabric.Image.filters.Convolute,
15 - /** @lends Convolute.prototype */ {
16 - /**
17 - * Filter type
18 - * @param {String} type
19 - * @default
20 - */
21 - type: 'Blur',
22 -
23 - /**
24 - * constructor
25 - * @override
26 - */
27 - initialize() {
28 - this.matrix = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
29 - },
30 - }
31 -);
32 -
33 -export default Blur;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview ColorFilter extending fabric.Image.filters.BaseFilter
4 - */
5 -import fabric from 'fabric';
6 -
7 -/**
8 - * ColorFilter object
9 - * @class ColorFilter
10 - * @extends {fabric.Image.filters.BaseFilter}
11 - * @ignore
12 - */
13 -const ColorFilter = fabric.util.createClass(
14 - fabric.Image.filters.BaseFilter,
15 - /** @lends BaseFilter.prototype */ {
16 - /**
17 - * Filter type
18 - * @param {String} type
19 - * @default
20 - */
21 - type: 'ColorFilter',
22 -
23 - /**
24 - * Constructor
25 - * @member fabric.Image.filters.ColorFilter.prototype
26 - * @param {Object} [options] Options object
27 - * @param {Number} [options.color='#FFFFFF'] Value of color (0...255)
28 - * @param {Number} [options.threshold=45] Value of threshold (0...255)
29 - * @override
30 - */
31 - initialize(options) {
32 - if (!options) {
33 - options = {};
34 - }
35 - this.color = options.color || '#FFFFFF';
36 - this.threshold = options.threshold || 45;
37 - this.x = options.x || null;
38 - this.y = options.y || null;
39 - },
40 -
41 - /**
42 - * Applies filter to canvas element
43 - * @param {Object} canvas Canvas object passed by fabric
44 - */
45 - // eslint-disable-next-line complexity
46 - applyTo(canvas) {
47 - const { canvasEl } = canvas;
48 - const context = canvasEl.getContext('2d');
49 - const imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height);
50 - const { data } = imageData;
51 - const { threshold } = this;
52 - let filterColor = fabric.Color.sourceFromHex(this.color);
53 - let i, len;
54 -
55 - if (this.x && this.y) {
56 - filterColor = this._getColor(imageData, this.x, this.y);
57 - }
58 -
59 - for (i = 0, len = data.length; i < len; i += 4) {
60 - if (
61 - this._isOutsideThreshold(data[i], filterColor[0], threshold) ||
62 - this._isOutsideThreshold(data[i + 1], filterColor[1], threshold) ||
63 - this._isOutsideThreshold(data[i + 2], filterColor[2], threshold)
64 - ) {
65 - continue;
66 - }
67 - data[i] = data[i + 1] = data[i + 2] = data[i + 3] = 0;
68 - }
69 - context.putImageData(imageData, 0, 0);
70 - },
71 -
72 - /**
73 - * Check color if it is within threshold
74 - * @param {Number} color1 source color
75 - * @param {Number} color2 filtering color
76 - * @param {Number} threshold threshold
77 - * @returns {boolean} true if within threshold or false
78 - */
79 - _isOutsideThreshold(color1, color2, threshold) {
80 - const diff = color1 - color2;
81 -
82 - return Math.abs(diff) > threshold;
83 - },
84 -
85 - /**
86 - * Get color at (x, y)
87 - * @param {Object} imageData of canvas
88 - * @param {Number} x left position
89 - * @param {Number} y top position
90 - * @returns {Array} color array
91 - */
92 - _getColor(imageData, x, y) {
93 - const color = [0, 0, 0, 0];
94 - const { data, width } = imageData;
95 - const bytes = 4;
96 - const position = (width * y + x) * bytes;
97 -
98 - color[0] = data[position];
99 - color[1] = data[position + 1];
100 - color[2] = data[position + 2];
101 - color[3] = data[position + 3];
102 -
103 - return color;
104 - },
105 - }
106 -);
107 -
108 -export default ColorFilter;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Cropzone extending fabric.Rect
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import fabric from 'fabric';
7 -import { clamp } from '../util';
8 -import { eventNames as events } from '../consts';
9 -
10 -const CORNER_TYPE_TOP_LEFT = 'tl';
11 -const CORNER_TYPE_TOP_RIGHT = 'tr';
12 -const CORNER_TYPE_MIDDLE_TOP = 'mt';
13 -const CORNER_TYPE_MIDDLE_LEFT = 'ml';
14 -const CORNER_TYPE_MIDDLE_RIGHT = 'mr';
15 -const CORNER_TYPE_MIDDLE_BOTTOM = 'mb';
16 -const CORNER_TYPE_BOTTOM_LEFT = 'bl';
17 -const CORNER_TYPE_BOTTOM_RIGHT = 'br';
18 -const CORNER_TYPE_LIST = [
19 - CORNER_TYPE_TOP_LEFT,
20 - CORNER_TYPE_TOP_RIGHT,
21 - CORNER_TYPE_MIDDLE_TOP,
22 - CORNER_TYPE_MIDDLE_LEFT,
23 - CORNER_TYPE_MIDDLE_RIGHT,
24 - CORNER_TYPE_MIDDLE_BOTTOM,
25 - CORNER_TYPE_BOTTOM_LEFT,
26 - CORNER_TYPE_BOTTOM_RIGHT,
27 -];
28 -const NOOP_FUNCTION = () => {};
29 -
30 -/**
31 - * Align with cropzone ratio
32 - * @param {string} selectedCorner - selected corner type
33 - * @returns {{width: number, height: number}}
34 - * @private
35 - */
36 -function cornerTypeValid(selectedCorner) {
37 - return CORNER_TYPE_LIST.indexOf(selectedCorner) >= 0;
38 -}
39 -
40 -/**
41 - * return scale basis type
42 - * @param {number} diffX - X distance of the cursor and corner.
43 - * @param {number} diffY - Y distance of the cursor and corner.
44 - * @returns {string}
45 - * @private
46 - */
47 -function getScaleBasis(diffX, diffY) {
48 - return diffX > diffY ? 'width' : 'height';
49 -}
50 -
51 -/**
52 - * Cropzone object
53 - * Issue: IE7, 8(with excanvas)
54 - * - Cropzone is a black zone without transparency.
55 - * @class Cropzone
56 - * @extends {fabric.Rect}
57 - * @ignore
58 - */
59 -const Cropzone = fabric.util.createClass(
60 - fabric.Rect,
61 - /** @lends Cropzone.prototype */ {
62 - /**
63 - * Constructor
64 - * @param {Object} canvas canvas
65 - * @param {Object} options Options object
66 - * @param {Object} extendsOptions object for extends "options"
67 - * @override
68 - */
69 - initialize(canvas, options, extendsOptions) {
70 - options = snippet.extend(options, extendsOptions);
71 - options.type = 'cropzone';
72 -
73 - this.callSuper('initialize', options);
74 - this._addEventHandler();
75 -
76 - this.canvas = canvas;
77 - this.options = options;
78 - },
79 - canvasEventDelegation(eventName) {
80 - let delegationState = 'unregisted';
81 - const isRegisted = this.canvasEventTrigger[eventName] !== NOOP_FUNCTION;
82 - if (isRegisted) {
83 - delegationState = 'registed';
84 - } else if ([events.OBJECT_MOVED, events.OBJECT_SCALED].indexOf(eventName) < 0) {
85 - delegationState = 'none';
86 - }
87 -
88 - return delegationState;
89 - },
90 - canvasEventRegister(eventName, eventTrigger) {
91 - this.canvasEventTrigger[eventName] = eventTrigger;
92 - },
93 - _addEventHandler() {
94 - this.canvasEventTrigger = {
95 - [events.OBJECT_MOVED]: NOOP_FUNCTION,
96 - [events.OBJECT_SCALED]: NOOP_FUNCTION,
97 - };
98 - this.on({
99 - moving: this._onMoving.bind(this),
100 - scaling: this._onScaling.bind(this),
101 - });
102 - },
103 - _renderCropzone(ctx) {
104 - const cropzoneDashLineWidth = 7;
105 - const cropzoneDashLineOffset = 7;
106 -
107 - // Calc original scale
108 - const originalFlipX = this.flipX ? -1 : 1;
109 - const originalFlipY = this.flipY ? -1 : 1;
110 - const originalScaleX = originalFlipX / this.scaleX;
111 - const originalScaleY = originalFlipY / this.scaleY;
112 -
113 - // Set original scale
114 - ctx.scale(originalScaleX, originalScaleY);
115 -
116 - // Render outer rect
117 - this._fillOuterRect(ctx, 'rgba(0, 0, 0, 0.5)');
118 -
119 - if (this.options.lineWidth) {
120 - this._fillInnerRect(ctx);
121 - this._strokeBorder(ctx, 'rgb(255, 255, 255)', {
122 - lineWidth: this.options.lineWidth,
123 - });
124 - } else {
125 - // Black dash line
126 - this._strokeBorder(ctx, 'rgb(0, 0, 0)', {
127 - lineDashWidth: cropzoneDashLineWidth,
128 - });
129 -
130 - // White dash line
131 - this._strokeBorder(ctx, 'rgb(255, 255, 255)', {
132 - lineDashWidth: cropzoneDashLineWidth,
133 - lineDashOffset: cropzoneDashLineOffset,
134 - });
135 - }
136 -
137 - // Reset scale
138 - ctx.scale(1 / originalScaleX, 1 / originalScaleY);
139 - },
140 -
141 - /**
142 - * Render Crop-zone
143 - * @private
144 - * @override
145 - */
146 - _render(ctx) {
147 - this.callSuper('_render', ctx);
148 -
149 - this._renderCropzone(ctx);
150 - },
151 -
152 - /**
153 - * Cropzone-coordinates with outer rectangle
154 - *
155 - * x0 x1 x2 x3
156 - * y0 +--------------------------+
157 - * |///////|//////////|///////| // <--- "Outer-rectangle"
158 - * |///////|//////////|///////|
159 - * y1 +-------+----------+-------+
160 - * |///////| Cropzone |///////| Cropzone is the "Inner-rectangle"
161 - * |///////| (0, 0) |///////| Center point (0, 0)
162 - * y2 +-------+----------+-------+
163 - * |///////|//////////|///////|
164 - * |///////|//////////|///////|
165 - * y3 +--------------------------+
166 - *
167 - * @typedef {{x: Array<number>, y: Array<number>}} cropzoneCoordinates
168 - * @ignore
169 - */
170 -
171 - /**
172 - * Fill outer rectangle
173 - * @param {CanvasRenderingContext2D} ctx - Context
174 - * @param {string|CanvasGradient|CanvasPattern} fillStyle - Fill-style
175 - * @private
176 - */
177 - _fillOuterRect(ctx, fillStyle) {
178 - const { x, y } = this._getCoordinates();
179 -
180 - ctx.save();
181 - ctx.fillStyle = fillStyle;
182 - ctx.beginPath();
183 -
184 - // Outer rectangle
185 - // Numbers are +/-1 so that overlay edges don't get blurry.
186 - ctx.moveTo(x[0] - 1, y[0] - 1);
187 - ctx.lineTo(x[3] + 1, y[0] - 1);
188 - ctx.lineTo(x[3] + 1, y[3] + 1);
189 - ctx.lineTo(x[0] - 1, y[3] + 1);
190 - ctx.lineTo(x[0] - 1, y[0] - 1);
191 - ctx.closePath();
192 -
193 - // Inner rectangle
194 - ctx.moveTo(x[1], y[1]);
195 - ctx.lineTo(x[1], y[2]);
196 - ctx.lineTo(x[2], y[2]);
197 - ctx.lineTo(x[2], y[1]);
198 - ctx.lineTo(x[1], y[1]);
199 - ctx.closePath();
200 -
201 - ctx.fill();
202 - ctx.restore();
203 - },
204 -
205 - /**
206 - * Draw Inner grid line
207 - * @param {CanvasRenderingContext2D} ctx - Context
208 - * @private
209 - */
210 - _fillInnerRect(ctx) {
211 - const { x: outerX, y: outerY } = this._getCoordinates();
212 - const x = this._caculateInnerPosition(outerX, (outerX[2] - outerX[1]) / 3);
213 - const y = this._caculateInnerPosition(outerY, (outerY[2] - outerY[1]) / 3);
214 -
215 - ctx.save();
216 - ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
217 - ctx.lineWidth = this.options.lineWidth;
218 - ctx.beginPath();
219 -
220 - ctx.moveTo(x[0], y[1]);
221 - ctx.lineTo(x[3], y[1]);
222 -
223 - ctx.moveTo(x[0], y[2]);
224 - ctx.lineTo(x[3], y[2]);
225 -
226 - ctx.moveTo(x[1], y[0]);
227 - ctx.lineTo(x[1], y[3]);
228 -
229 - ctx.moveTo(x[2], y[0]);
230 - ctx.lineTo(x[2], y[3]);
231 - ctx.stroke();
232 - ctx.closePath();
233 -
234 - ctx.restore();
235 - },
236 -
237 - /**
238 - * Calculate Inner Position
239 - * @param {Array} outer - outer position
240 - * @param {number} size - interval for calculate
241 - * @returns {Array} - inner position
242 - * @private
243 - */
244 - _caculateInnerPosition(outer, size) {
245 - const position = [];
246 - position[0] = outer[1];
247 - position[1] = outer[1] + size;
248 - position[2] = outer[1] + size * 2;
249 - position[3] = outer[2];
250 -
251 - return position;
252 - },
253 -
254 - /**
255 - * Get coordinates
256 - * @returns {cropzoneCoordinates} - {@link cropzoneCoordinates}
257 - * @private
258 - */
259 - _getCoordinates() {
260 - const { canvas, width, height, left, top } = this;
261 - const halfWidth = width / 2;
262 - const halfHeight = height / 2;
263 - const canvasHeight = canvas.getHeight(); // fabric object
264 - const canvasWidth = canvas.getWidth(); // fabric object
265 -
266 - return {
267 - x: snippet.map(
268 - [
269 - -(halfWidth + left), // x0
270 - -halfWidth, // x1
271 - halfWidth, // x2
272 - halfWidth + (canvasWidth - left - width), // x3
273 - ],
274 - Math.ceil
275 - ),
276 - y: snippet.map(
277 - [
278 - -(halfHeight + top), // y0
279 - -halfHeight, // y1
280 - halfHeight, // y2
281 - halfHeight + (canvasHeight - top - height), // y3
282 - ],
283 - Math.ceil
284 - ),
285 - };
286 - },
287 -
288 - /**
289 - * Stroke border
290 - * @param {CanvasRenderingContext2D} ctx - Context
291 - * @param {string|CanvasGradient|CanvasPattern} strokeStyle - Stroke-style
292 - * @param {number} lineDashWidth - Dash width
293 - * @param {number} [lineDashOffset] - Dash offset
294 - * @param {number} [lineWidth] - line width
295 - * @private
296 - */
297 - _strokeBorder(ctx, strokeStyle, { lineDashWidth, lineDashOffset, lineWidth }) {
298 - const halfWidth = this.width / 2;
299 - const halfHeight = this.height / 2;
300 -
301 - ctx.save();
302 - ctx.strokeStyle = strokeStyle;
303 -
304 - if (ctx.setLineDash) {
305 - ctx.setLineDash([lineDashWidth, lineDashWidth]);
306 - }
307 - if (lineDashOffset) {
308 - ctx.lineDashOffset = lineDashOffset;
309 - }
310 - if (lineWidth) {
311 - ctx.lineWidth = lineWidth;
312 - }
313 -
314 - ctx.beginPath();
315 - ctx.moveTo(-halfWidth, -halfHeight);
316 - ctx.lineTo(halfWidth, -halfHeight);
317 - ctx.lineTo(halfWidth, halfHeight);
318 - ctx.lineTo(-halfWidth, halfHeight);
319 - ctx.lineTo(-halfWidth, -halfHeight);
320 - ctx.stroke();
321 -
322 - ctx.restore();
323 - },
324 -
325 - /**
326 - * onMoving event listener
327 - * @private
328 - */
329 - _onMoving() {
330 - const { height, width, left, top } = this;
331 - const maxLeft = this.canvas.getWidth() - width;
332 - const maxTop = this.canvas.getHeight() - height;
333 -
334 - this.left = clamp(left, 0, maxLeft);
335 - this.top = clamp(top, 0, maxTop);
336 -
337 - this.canvasEventTrigger[events.OBJECT_MOVED](this);
338 - },
339 -
340 - /**
341 - * onScaling event listener
342 - * @param {{e: MouseEvent}} fEvent - Fabric event
343 - * @private
344 - */
345 - _onScaling(fEvent) {
346 - const selectedCorner = fEvent.transform.corner;
347 - const pointer = this.canvas.getPointer(fEvent.e);
348 - const settings = this._calcScalingSizeFromPointer(pointer, selectedCorner);
349 -
350 - // On scaling cropzone,
351 - // change real width and height and fix scaleFactor to 1
352 - this.scale(1).set(settings);
353 -
354 - this.canvasEventTrigger[events.OBJECT_SCALED](this);
355 - },
356 -
357 - /**
358 - * Calc scaled size from mouse pointer with selected corner
359 - * @param {{x: number, y: number}} pointer - Mouse position
360 - * @param {string} selectedCorner - selected corner type
361 - * @returns {Object} Having left or(and) top or(and) width or(and) height.
362 - * @private
363 - */
364 - _calcScalingSizeFromPointer(pointer, selectedCorner) {
365 - const isCornerTypeValid = cornerTypeValid(selectedCorner);
366 -
367 - return isCornerTypeValid && this._resizeCropZone(pointer, selectedCorner);
368 - },
369 -
370 - /**
371 - * Align with cropzone ratio
372 - * @param {number} width - cropzone width
373 - * @param {number} height - cropzone height
374 - * @param {number} maxWidth - limit max width
375 - * @param {number} maxHeight - limit max height
376 - * @param {number} scaleTo - cropzone ratio
377 - * @returns {{width: number, height: number}}
378 - * @private
379 - */
380 - adjustRatioCropzoneSize({ width, height, leftMaker, topMaker, maxWidth, maxHeight, scaleTo }) {
381 - width = maxWidth ? clamp(width, 1, maxWidth) : width;
382 - height = maxHeight ? clamp(height, 1, maxHeight) : height;
383 -
384 - if (!this.presetRatio) {
385 - return {
386 - width,
387 - height,
388 - left: leftMaker(width),
389 - top: topMaker(height),
390 - };
391 - }
392 -
393 - if (scaleTo === 'width') {
394 - height = width / this.presetRatio;
395 - } else {
396 - width = height * this.presetRatio;
397 - }
398 -
399 - const maxScaleFactor = Math.min(maxWidth / width, maxHeight / height);
400 - if (maxScaleFactor <= 1) {
401 - [width, height] = [width, height].map((v) => v * maxScaleFactor);
402 - }
403 -
404 - return {
405 - width,
406 - height,
407 - left: leftMaker(width),
408 - top: topMaker(height),
409 - };
410 - },
411 -
412 - /**
413 - * Get dimension last state cropzone
414 - * @returns {{rectTop: number, rectLeft: number, rectWidth: number, rectHeight: number}}
415 - * @private
416 - */
417 - _getCropzoneRectInfo() {
418 - const { width: canvasWidth, height: canvasHeight } = this.canvas;
419 - const {
420 - top: rectTop,
421 - left: rectLeft,
422 - width: rectWidth,
423 - height: rectHeight,
424 - } = this.getBoundingRect(false, true);
425 -
426 - return {
427 - rectTop,
428 - rectLeft,
429 - rectWidth,
430 - rectHeight,
431 - rectRight: rectLeft + rectWidth,
432 - rectBottom: rectTop + rectHeight,
433 - canvasWidth,
434 - canvasHeight,
435 - };
436 - },
437 -
438 - /**
439 - * Calc scaling dimension
440 - * @param {Object} position - Mouse position
441 - * @param {string} corner - corner type
442 - * @returns {{left: number, top: number, width: number, height: number}}
443 - * @private
444 - */
445 - _resizeCropZone({ x, y }, corner) {
446 - const {
447 - rectWidth,
448 - rectHeight,
449 - rectTop,
450 - rectLeft,
451 - rectBottom,
452 - rectRight,
453 - canvasWidth,
454 - canvasHeight,
455 - } = this._getCropzoneRectInfo();
456 -
457 - const resizeInfoMap = {
458 - tl: {
459 - width: rectRight - x,
460 - height: rectBottom - y,
461 - leftMaker: (newWidth) => rectRight - newWidth,
462 - topMaker: (newHeight) => rectBottom - newHeight,
463 - maxWidth: rectRight,
464 - maxHeight: rectBottom,
465 - scaleTo: getScaleBasis(rectLeft - x, rectTop - y),
466 - },
467 - tr: {
468 - width: x - rectLeft,
469 - height: rectBottom - y,
470 - leftMaker: () => rectLeft,
471 - topMaker: (newHeight) => rectBottom - newHeight,
472 - maxWidth: canvasWidth - rectLeft,
473 - maxHeight: rectBottom,
474 - scaleTo: getScaleBasis(x - rectRight, rectTop - y),
475 - },
476 - mt: {
477 - width: rectWidth,
478 - height: rectBottom - y,
479 - leftMaker: () => rectLeft,
480 - topMaker: (newHeight) => rectBottom - newHeight,
481 - maxWidth: canvasWidth - rectLeft,
482 - maxHeight: rectBottom,
483 - scaleTo: 'height',
484 - },
485 - ml: {
486 - width: rectRight - x,
487 - height: rectHeight,
488 - leftMaker: (newWidth) => rectRight - newWidth,
489 - topMaker: () => rectTop,
490 - maxWidth: rectRight,
491 - maxHeight: canvasHeight - rectTop,
492 - scaleTo: 'width',
493 - },
494 - mr: {
495 - width: x - rectLeft,
496 - height: rectHeight,
497 - leftMaker: () => rectLeft,
498 - topMaker: () => rectTop,
499 - maxWidth: canvasWidth - rectLeft,
500 - maxHeight: canvasHeight - rectTop,
501 - scaleTo: 'width',
502 - },
503 - mb: {
504 - width: rectWidth,
505 - height: y - rectTop,
506 - leftMaker: () => rectLeft,
507 - topMaker: () => rectTop,
508 - maxWidth: canvasWidth - rectLeft,
509 - maxHeight: canvasHeight - rectTop,
510 - scaleTo: 'height',
511 - },
512 - bl: {
513 - width: rectRight - x,
514 - height: y - rectTop,
515 - leftMaker: (newWidth) => rectRight - newWidth,
516 - topMaker: () => rectTop,
517 - maxWidth: rectRight,
518 - maxHeight: canvasHeight - rectTop,
519 - scaleTo: getScaleBasis(rectLeft - x, y - rectBottom),
520 - },
521 - br: {
522 - width: x - rectLeft,
523 - height: y - rectTop,
524 - leftMaker: () => rectLeft,
525 - topMaker: () => rectTop,
526 - maxWidth: canvasWidth - rectLeft,
527 - maxHeight: canvasHeight - rectTop,
528 - scaleTo: getScaleBasis(x - rectRight, y - rectBottom),
529 - },
530 - };
531 -
532 - return this.adjustRatioCropzoneSize(resizeInfoMap[corner]);
533 - },
534 -
535 - /**
536 - * Return the whether this cropzone is valid
537 - * @returns {boolean}
538 - */
539 - isValid() {
540 - return this.left >= 0 && this.top >= 0 && this.width > 0 && this.height > 0;
541 - },
542 - }
543 -);
544 -
545 -export default Cropzone;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Emboss extending fabric.Image.filters.Convolute
4 - */
5 -import fabric from 'fabric';
6 -
7 -/**
8 - * Emboss object
9 - * @class Emboss
10 - * @extends {fabric.Image.filters.Convolute}
11 - * @ignore
12 - */
13 -const Emboss = fabric.util.createClass(
14 - fabric.Image.filters.Convolute,
15 - /** @lends Convolute.prototype */ {
16 - /**
17 - * Filter type
18 - * @param {String} type
19 - * @default
20 - */
21 - type: 'Emboss',
22 -
23 - /**
24 - * constructor
25 - * @override
26 - */
27 - initialize() {
28 - const matrix = [1, 1, 1, 1, 0.7, -1, -1, -1, -1];
29 - this.matrix = matrix;
30 - },
31 - }
32 -);
33 -
34 -export default Emboss;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Mask extending fabric.Image.filters.Mask
4 - */
5 -import fabric from 'fabric';
6 -
7 -/**
8 - * Mask object
9 - * @class Mask
10 - * @extends {fabric.Image.filters.BlendImage}
11 - * @ignore
12 - */
13 -const Mask = fabric.util.createClass(
14 - fabric.Image.filters.BlendImage,
15 - /** @lends Mask.prototype */ {
16 - /**
17 - * Apply filter to canvas element
18 - * @param {Object} pipelineState - Canvas element to apply filter
19 - * @override
20 - */
21 - applyTo(pipelineState) {
22 - if (!this.mask) {
23 - return;
24 - }
25 -
26 - const canvas = pipelineState.canvasEl;
27 - const { width, height } = canvas;
28 - const maskCanvasEl = this._createCanvasOfMask(width, height);
29 - const ctx = canvas.getContext('2d');
30 - const maskCtx = maskCanvasEl.getContext('2d');
31 - const imageData = ctx.getImageData(0, 0, width, height);
32 -
33 - this._drawMask(maskCtx, canvas, ctx);
34 - this._mapData(maskCtx, imageData, width, height);
35 -
36 - pipelineState.imageData = imageData;
37 - },
38 -
39 - /**
40 - * Create canvas of mask image
41 - * @param {number} width - Width of main canvas
42 - * @param {number} height - Height of main canvas
43 - * @returns {HTMLElement} Canvas element
44 - * @private
45 - */
46 - _createCanvasOfMask(width, height) {
47 - const maskCanvasEl = fabric.util.createCanvasElement();
48 -
49 - maskCanvasEl.width = width;
50 - maskCanvasEl.height = height;
51 -
52 - return maskCanvasEl;
53 - },
54 -
55 - /**
56 - * Draw mask image on canvas element
57 - * @param {Object} maskCtx - Context of mask canvas
58 - * @private
59 - */
60 - _drawMask(maskCtx) {
61 - const { mask } = this;
62 - const maskImg = mask.getElement();
63 - const { angle, left, scaleX, scaleY, top } = mask;
64 -
65 - maskCtx.save();
66 - maskCtx.translate(left, top);
67 - maskCtx.rotate((angle * Math.PI) / 180);
68 - maskCtx.scale(scaleX, scaleY);
69 - maskCtx.drawImage(maskImg, -maskImg.width / 2, -maskImg.height / 2);
70 - maskCtx.restore();
71 - },
72 -
73 - /**
74 - * Map mask image data to source image data
75 - * @param {Object} maskCtx - Context of mask canvas
76 - * @param {Object} imageData - Data of source image
77 - * @param {number} width - Width of main canvas
78 - * @param {number} height - Height of main canvas
79 - * @private
80 - */
81 - _mapData(maskCtx, imageData, width, height) {
82 - const { data, height: imgHeight, width: imgWidth } = imageData;
83 - const sourceData = data;
84 - const len = imgWidth * imgHeight * 4;
85 - const maskData = maskCtx.getImageData(0, 0, width, height).data;
86 -
87 - for (let i = 0; i < len; i += 4) {
88 - sourceData[i + 3] = maskData[i]; // adjust value of alpha data
89 - }
90 - },
91 - }
92 -);
93 -
94 -export default Mask;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Sharpen extending fabric.Image.filters.Convolute
4 - */
5 -import fabric from 'fabric';
6 -
7 -/**
8 - * Sharpen object
9 - * @class Sharpen
10 - * @extends {fabric.Image.filters.Convolute}
11 - * @ignore
12 - */
13 -const Sharpen = fabric.util.createClass(
14 - fabric.Image.filters.Convolute,
15 - /** @lends Convolute.prototype */ {
16 - /**
17 - * Filter type
18 - * @param {String} type
19 - * @default
20 - */
21 - type: 'Sharpen',
22 -
23 - /**
24 - * constructor
25 - * @override
26 - */
27 - initialize() {
28 - const matrix = [0, -1, 0, -1, 5, -1, 0, -1, 0];
29 - this.matrix = matrix;
30 - },
31 - }
32 -);
33 -
34 -export default Sharpen;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Command factory
4 - */
5 -import Command from '../interface/command';
6 -
7 -const commands = {};
8 -
9 -/**
10 - * Create a command
11 - * @param {string} name - Command name
12 - * @param {...*} args - Arguments for creating command
13 - * @returns {Command}
14 - * @ignore
15 - */
16 -function create(name, ...args) {
17 - const actions = commands[name];
18 - if (actions) {
19 - return new Command(actions, args);
20 - }
21 -
22 - return null;
23 -}
24 -
25 -/**
26 - * Register a command with name as a key
27 - * @param {Object} command - {name:{string}, execute: {function}, undo: {function}}
28 - * @param {string} command.name - command name
29 - * @param {function} command.execute - executable function
30 - * @param {function} command.undo - undo function
31 - * @ignore
32 - */
33 -function register(command) {
34 - commands[command.name] = command;
35 -}
36 -
37 -export default {
38 - create,
39 - register,
40 -};
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Error-message factory
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import { keyMirror } from '../util';
7 -
8 -const types = keyMirror('UN_IMPLEMENTATION', 'NO_COMPONENT_NAME');
9 -const messages = {
10 - UN_IMPLEMENTATION: 'Should implement a method: ',
11 - NO_COMPONENT_NAME: 'Should set a component name',
12 -};
13 -const map = {
14 - UN_IMPLEMENTATION(methodName) {
15 - return messages.UN_IMPLEMENTATION + methodName;
16 - },
17 - NO_COMPONENT_NAME() {
18 - return messages.NO_COMPONENT_NAME;
19 - },
20 -};
21 -
22 -export default {
23 - types: snippet.extend({}, types),
24 -
25 - create(type, ...args) {
26 - type = type.toLowerCase();
27 - const func = map[type];
28 -
29 - return func(...args);
30 - },
31 -};
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Graphics module
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import fabric from 'fabric';
7 -import ImageLoader from './component/imageLoader';
8 -import Cropper from './component/cropper';
9 -import Flip from './component/flip';
10 -import Rotation from './component/rotation';
11 -import FreeDrawing from './component/freeDrawing';
12 -import Line from './component/line';
13 -import Text from './component/text';
14 -import Icon from './component/icon';
15 -import Filter from './component/filter';
16 -import Shape from './component/shape';
17 -import CropperDrawingMode from './drawingMode/cropper';
18 -import FreeDrawingMode from './drawingMode/freeDrawing';
19 -import LineDrawingMode from './drawingMode/lineDrawing';
20 -import ShapeDrawingMode from './drawingMode/shape';
21 -import TextDrawingMode from './drawingMode/text';
22 -import IconDrawingMode from './drawingMode/icon';
23 -import { getProperties, includes, isShape, Promise } from './util';
24 -import {
25 - componentNames as components,
26 - eventNames as events,
27 - drawingModes,
28 - fObjectOptions,
29 -} from './consts';
30 -import {
31 - makeSelectionUndoData,
32 - makeSelectionUndoDatum,
33 - setCachedUndoDataForDimension,
34 -} from './helper/selectionModifyHelper';
35 -
36 -const {
37 - extend,
38 - stamp,
39 - isArray,
40 - isString,
41 - forEachArray,
42 - forEachOwnProperties,
43 - CustomEvents,
44 -} = snippet;
45 -const DEFAULT_CSS_MAX_WIDTH = 1000;
46 -const DEFAULT_CSS_MAX_HEIGHT = 800;
47 -const EXTRA_PX_FOR_PASTE = 10;
48 -
49 -const cssOnly = {
50 - cssOnly: true,
51 -};
52 -const backstoreOnly = {
53 - backstoreOnly: true,
54 -};
55 -
56 -/**
57 - * Graphics class
58 - * @class
59 - * @param {string|HTMLElement} wrapper - Wrapper's element or selector
60 - * @param {Object} [option] - Canvas max width & height of css
61 - * @param {number} option.cssMaxWidth - Canvas css-max-width
62 - * @param {number} option.cssMaxHeight - Canvas css-max-height
63 - * @ignore
64 - */
65 -class Graphics {
66 - constructor(element, { cssMaxWidth, cssMaxHeight } = {}) {
67 - /**
68 - * Fabric image instance
69 - * @type {fabric.Image}
70 - */
71 - this.canvasImage = null;
72 -
73 - /**
74 - * Max width of canvas elements
75 - * @type {number}
76 - */
77 - this.cssMaxWidth = cssMaxWidth || DEFAULT_CSS_MAX_WIDTH;
78 -
79 - /**
80 - * Max height of canvas elements
81 - * @type {number}
82 - */
83 - this.cssMaxHeight = cssMaxHeight || DEFAULT_CSS_MAX_HEIGHT;
84 -
85 - /**
86 - * cropper Selection Style
87 - * @type {Object}
88 - */
89 - this.cropSelectionStyle = {};
90 -
91 - /**
92 - * target fabric object for copy paste feature
93 - * @type {fabric.Object}
94 - * @private
95 - */
96 - this.targetObjectForCopyPaste = null;
97 -
98 - /**
99 - * Image name
100 - * @type {string}
101 - */
102 - this.imageName = '';
103 -
104 - /**
105 - * Object Map
106 - * @type {Object}
107 - * @private
108 - */
109 - this._objects = {};
110 -
111 - /**
112 - * Fabric-Canvas instance
113 - * @type {fabric.Canvas}
114 - * @private
115 - */
116 - this._canvas = null;
117 -
118 - /**
119 - * Drawing mode
120 - * @type {string}
121 - * @private
122 - */
123 - this._drawingMode = drawingModes.NORMAL;
124 -
125 - /**
126 - * DrawingMode map
127 - * @type {Object.<string, DrawingMode>}
128 - * @private
129 - */
130 - this._drawingModeMap = {};
131 -
132 - /**
133 - * Component map
134 - * @type {Object.<string, Component>}
135 - * @private
136 - */
137 - this._componentMap = {};
138 -
139 - /**
140 - * fabric event handlers
141 - * @type {Object.<string, function>}
142 - * @private
143 - */
144 - this._handler = {
145 - onMouseDown: this._onMouseDown.bind(this),
146 - onObjectAdded: this._onObjectAdded.bind(this),
147 - onObjectRemoved: this._onObjectRemoved.bind(this),
148 - onObjectMoved: this._onObjectMoved.bind(this),
149 - onObjectScaled: this._onObjectScaled.bind(this),
150 - onObjectModified: this._onObjectModified.bind(this),
151 - onObjectRotated: this._onObjectRotated.bind(this),
152 - onObjectSelected: this._onObjectSelected.bind(this),
153 - onPathCreated: this._onPathCreated.bind(this),
154 - onSelectionCleared: this._onSelectionCleared.bind(this),
155 - onSelectionCreated: this._onSelectionCreated.bind(this),
156 - };
157 -
158 - this._setObjectCachingToFalse();
159 - this._setCanvasElement(element);
160 - this._createDrawingModeInstances();
161 - this._createComponents();
162 - this._attachCanvasEvents();
163 - }
164 -
165 - /**
166 - * Destroy canvas element
167 - */
168 - destroy() {
169 - const { wrapperEl } = this._canvas;
170 -
171 - this._canvas.clear();
172 -
173 - wrapperEl.parentNode.removeChild(wrapperEl);
174 - }
175 -
176 - /**
177 - * Deactivates all objects on canvas
178 - * @returns {Graphics} this
179 - */
180 - deactivateAll() {
181 - this._canvas.discardActiveObject();
182 -
183 - return this;
184 - }
185 -
186 - /**
187 - * Renders all objects on canvas
188 - * @returns {Graphics} this
189 - */
190 - renderAll() {
191 - this._canvas.renderAll();
192 -
193 - return this;
194 - }
195 -
196 - /**
197 - * Adds objects on canvas
198 - * @param {Object|Array} objects - objects
199 - */
200 - add(objects) {
201 - let theArgs = [];
202 - if (isArray(objects)) {
203 - theArgs = objects;
204 - } else {
205 - theArgs.push(objects);
206 - }
207 -
208 - this._canvas.add(...theArgs);
209 - }
210 -
211 - /**
212 - * Removes the object or group
213 - * @param {Object} target - graphics object or group
214 - * @returns {boolean} true if contains or false
215 - */
216 - contains(target) {
217 - return this._canvas.contains(target);
218 - }
219 -
220 - /**
221 - * Gets all objects or group
222 - * @returns {Array} all objects, shallow copy
223 - */
224 - getObjects() {
225 - return this._canvas.getObjects().slice();
226 - }
227 -
228 - /**
229 - * Get an object by id
230 - * @param {number} id - object id
231 - * @returns {fabric.Object} object corresponding id
232 - */
233 - getObject(id) {
234 - return this._objects[id];
235 - }
236 -
237 - /**
238 - * Removes the object or group
239 - * @param {Object} target - graphics object or group
240 - */
241 - remove(target) {
242 - this._canvas.remove(target);
243 - }
244 -
245 - /**
246 - * Removes all object or group
247 - * @param {boolean} includesBackground - remove the background image or not
248 - * @returns {Array} all objects array which is removed
249 - */
250 - removeAll(includesBackground) {
251 - const canvas = this._canvas;
252 - const objects = canvas.getObjects().slice();
253 - canvas.remove(...this._canvas.getObjects());
254 -
255 - if (includesBackground) {
256 - canvas.clear();
257 - }
258 -
259 - return objects;
260 - }
261 -
262 - /**
263 - * Removes an object or group by id
264 - * @param {number} id - object id
265 - * @returns {Array} removed objects
266 - */
267 - removeObjectById(id) {
268 - const objects = [];
269 - const canvas = this._canvas;
270 - const target = this.getObject(id);
271 - const isValidGroup = target && target.isType('group') && !target.isEmpty();
272 -
273 - if (isValidGroup) {
274 - canvas.discardActiveObject(); // restore states for each objects
275 - target.forEachObject((obj) => {
276 - objects.push(obj);
277 - canvas.remove(obj);
278 - });
279 - } else if (canvas.contains(target)) {
280 - objects.push(target);
281 - canvas.remove(target);
282 - }
283 -
284 - return objects;
285 - }
286 -
287 - /**
288 - * Get an id by object instance
289 - * @param {fabric.Object} object object
290 - * @returns {number} object id if it exists or null
291 - */
292 - getObjectId(object) {
293 - let key = null;
294 - for (key in this._objects) {
295 - if (this._objects.hasOwnProperty(key)) {
296 - if (object === this._objects[key]) {
297 - return key;
298 - }
299 - }
300 - }
301 -
302 - return null;
303 - }
304 -
305 - /**
306 - * Gets an active object or group
307 - * @returns {Object} active object or group instance
308 - */
309 - getActiveObject() {
310 - return this._canvas._activeObject;
311 - }
312 -
313 - /**
314 - * Returns the object ID to delete the object.
315 - * @returns {number} object id for remove
316 - */
317 - getActiveObjectIdForRemove() {
318 - const activeObject = this.getActiveObject();
319 - const { type, left, top } = activeObject;
320 - const isSelection = type === 'activeSelection';
321 -
322 - if (isSelection) {
323 - const group = new fabric.Group([...activeObject.getObjects()], {
324 - left,
325 - top,
326 - });
327 -
328 - return this._addFabricObject(group);
329 - }
330 -
331 - return this.getObjectId(activeObject);
332 - }
333 -
334 - /**
335 - * Verify that you are ready to erase the object.
336 - * @returns {boolean} ready for object remove
337 - */
338 - isReadyRemoveObject() {
339 - const activeObject = this.getActiveObject();
340 -
341 - return activeObject && !activeObject.isEditing;
342 - }
343 -
344 - /**
345 - * Gets an active group object
346 - * @returns {Object} active group object instance
347 - */
348 - getActiveObjects() {
349 - const activeObject = this._canvas._activeObject;
350 -
351 - return activeObject && activeObject.type === 'activeSelection' ? activeObject : null;
352 - }
353 -
354 - /**
355 - * Get Active object Selection from object ids
356 - * @param {Array.<Object>} objects - fabric objects
357 - * @returns {Object} target - target object group
358 - */
359 - getActiveSelectionFromObjects(objects) {
360 - const canvas = this.getCanvas();
361 -
362 - return new fabric.ActiveSelection(objects, { canvas });
363 - }
364 -
365 - /**
366 - * Activates an object or group
367 - * @param {Object} target - target object or group
368 - */
369 - setActiveObject(target) {
370 - this._canvas.setActiveObject(target);
371 - }
372 -
373 - /**
374 - * Set Crop selection style
375 - * @param {Object} style - Selection styles
376 - */
377 - setCropSelectionStyle(style) {
378 - this.cropSelectionStyle = style;
379 - }
380 -
381 - /**
382 - * Get component
383 - * @param {string} name - Component name
384 - * @returns {Component}
385 - */
386 - getComponent(name) {
387 - return this._componentMap[name];
388 - }
389 -
390 - /**
391 - * Get current drawing mode
392 - * @returns {string}
393 - */
394 - getDrawingMode() {
395 - return this._drawingMode;
396 - }
397 -
398 - /**
399 - * Start a drawing mode. If the current mode is not 'NORMAL', 'stopDrawingMode()' will be called first.
400 - * @param {String} mode Can be one of <I>'CROPPER', 'FREE_DRAWING', 'LINE', 'TEXT', 'SHAPE'</I>
401 - * @param {Object} [option] parameters of drawing mode, it's available with 'FREE_DRAWING', 'LINE_DRAWING'
402 - * @param {Number} [option.width] brush width
403 - * @param {String} [option.color] brush color
404 - * @returns {boolean} true if success or false
405 - */
406 - startDrawingMode(mode, option) {
407 - if (this._isSameDrawingMode(mode)) {
408 - return true;
409 - }
410 -
411 - // If the current mode is not 'NORMAL', 'stopDrawingMode()' will be called first.
412 - this.stopDrawingMode();
413 -
414 - const drawingModeInstance = this._getDrawingModeInstance(mode);
415 - if (drawingModeInstance && drawingModeInstance.start) {
416 - drawingModeInstance.start(this, option);
417 -
418 - this._drawingMode = mode;
419 - }
420 -
421 - return !!drawingModeInstance;
422 - }
423 -
424 - /**
425 - * Stop the current drawing mode and back to the 'NORMAL' mode
426 - */
427 - stopDrawingMode() {
428 - if (this._isSameDrawingMode(drawingModes.NORMAL)) {
429 - return;
430 - }
431 -
432 - const drawingModeInstance = this._getDrawingModeInstance(this.getDrawingMode());
433 - if (drawingModeInstance && drawingModeInstance.end) {
434 - drawingModeInstance.end(this);
435 - }
436 - this._drawingMode = drawingModes.NORMAL;
437 - }
438 -
439 - /**
440 - * To data url from canvas
441 - * @param {Object} options - options for toDataURL
442 - * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
443 - * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
444 - * @param {Number} [options.multiplier=1] Multiplier to scale by
445 - * @param {Number} [options.left] Cropping left offset. Introduced in fabric v1.2.14
446 - * @param {Number} [options.top] Cropping top offset. Introduced in fabric v1.2.14
447 - * @param {Number} [options.width] Cropping width. Introduced in fabric v1.2.14
448 - * @param {Number} [options.height] Cropping height. Introduced in fabric v1.2.14
449 - * @returns {string} A DOMString containing the requested data URI.
450 - */
451 - toDataURL(options) {
452 - const cropper = this.getComponent(components.CROPPER);
453 - cropper.changeVisibility(false);
454 -
455 - const dataUrl = this._canvas && this._canvas.toDataURL(options);
456 - cropper.changeVisibility(true);
457 -
458 - return dataUrl;
459 - }
460 -
461 - /**
462 - * Save image(background) of canvas
463 - * @param {string} name - Name of image
464 - * @param {?fabric.Image} canvasImage - Fabric image instance
465 - */
466 - setCanvasImage(name, canvasImage) {
467 - if (canvasImage) {
468 - stamp(canvasImage);
469 - }
470 - this.imageName = name;
471 - this.canvasImage = canvasImage;
472 - }
473 -
474 - /**
475 - * Set css max dimension
476 - * @param {{width: number, height: number}} maxDimension - Max width & Max height
477 - */
478 - setCssMaxDimension(maxDimension) {
479 - this.cssMaxWidth = maxDimension.width || this.cssMaxWidth;
480 - this.cssMaxHeight = maxDimension.height || this.cssMaxHeight;
481 - }
482 -
483 - /**
484 - * Adjust canvas dimension with scaling image
485 - */
486 - adjustCanvasDimension() {
487 - const canvasImage = this.canvasImage.scale(1);
488 - const { width, height } = canvasImage.getBoundingRect();
489 - const maxDimension = this._calcMaxDimension(width, height);
490 -
491 - this.setCanvasCssDimension({
492 - width: '100%',
493 - height: '100%', // Set height '' for IE9
494 - 'max-width': `${maxDimension.width}px`,
495 - 'max-height': `${maxDimension.height}px`,
496 - });
497 -
498 - this.setCanvasBackstoreDimension({
499 - width,
500 - height,
501 - });
502 - this._canvas.centerObject(canvasImage);
503 - }
504 -
505 - /**
506 - * Set canvas dimension - css only
507 - * {@link http://fabricjs.com/docs/fabric.Canvas.html#setDimensions}
508 - * @param {Object} dimension - Canvas css dimension
509 - */
510 - setCanvasCssDimension(dimension) {
511 - this._canvas.setDimensions(dimension, cssOnly);
512 - }
513 -
514 - /**
515 - * Set canvas dimension - backstore only
516 - * {@link http://fabricjs.com/docs/fabric.Canvas.html#setDimensions}
517 - * @param {Object} dimension - Canvas backstore dimension
518 - */
519 - setCanvasBackstoreDimension(dimension) {
520 - this._canvas.setDimensions(dimension, backstoreOnly);
521 - }
522 -
523 - /**
524 - * Set image properties
525 - * {@link http://fabricjs.com/docs/fabric.Image.html#set}
526 - * @param {Object} setting - Image properties
527 - * @param {boolean} [withRendering] - If true, The changed image will be reflected in the canvas
528 - */
529 - setImageProperties(setting, withRendering) {
530 - const { canvasImage } = this;
531 -
532 - if (!canvasImage) {
533 - return;
534 - }
535 -
536 - canvasImage.set(setting).setCoords();
537 - if (withRendering) {
538 - this._canvas.renderAll();
539 - }
540 - }
541 -
542 - /**
543 - * Returns canvas element of fabric.Canvas[[lower-canvas]]
544 - * @returns {HTMLCanvasElement}
545 - */
546 - getCanvasElement() {
547 - return this._canvas.getElement();
548 - }
549 -
550 - /**
551 - * Get fabric.Canvas instance
552 - * @returns {fabric.Canvas}
553 - * @private
554 - */
555 - getCanvas() {
556 - return this._canvas;
557 - }
558 -
559 - /**
560 - * Get canvasImage (fabric.Image instance)
561 - * @returns {fabric.Image}
562 - */
563 - getCanvasImage() {
564 - return this.canvasImage;
565 - }
566 -
567 - /**
568 - * Get image name
569 - * @returns {string}
570 - */
571 - getImageName() {
572 - return this.imageName;
573 - }
574 -
575 - /**
576 - * Add image object on canvas
577 - * @param {string} imgUrl - Image url to make object
578 - * @returns {Promise}
579 - */
580 - addImageObject(imgUrl) {
581 - const callback = this._callbackAfterLoadingImageObject.bind(this);
582 -
583 - return new Promise((resolve) => {
584 - fabric.Image.fromURL(
585 - imgUrl,
586 - (image) => {
587 - callback(image);
588 - resolve(this.createObjectProperties(image));
589 - },
590 - {
591 - crossOrigin: 'Anonymous',
592 - }
593 - );
594 - });
595 - }
596 -
597 - /**
598 - * Get center position of canvas
599 - * @returns {Object} {left, top}
600 - */
601 - getCenter() {
602 - return this._canvas.getCenter();
603 - }
604 -
605 - /**
606 - * Get cropped rect
607 - * @returns {Object} rect
608 - */
609 - getCropzoneRect() {
610 - return this.getComponent(components.CROPPER).getCropzoneRect();
611 - }
612 -
613 - /**
614 - * Get cropped rect
615 - * @param {number} [mode] cropzone rect mode
616 - */
617 - setCropzoneRect(mode) {
618 - this.getComponent(components.CROPPER).setCropzoneRect(mode);
619 - }
620 -
621 - /**
622 - * Get cropped image data
623 - * @param {Object} cropRect cropzone rect
624 - * @param {Number} cropRect.left left position
625 - * @param {Number} cropRect.top top position
626 - * @param {Number} cropRect.width width
627 - * @param {Number} cropRect.height height
628 - * @returns {?{imageName: string, url: string}} cropped Image data
629 - */
630 - getCroppedImageData(cropRect) {
631 - return this.getComponent(components.CROPPER).getCroppedImageData(cropRect);
632 - }
633 -
634 - /**
635 - * Set brush option
636 - * @param {Object} option brush option
637 - * @param {Number} option.width width
638 - * @param {String} option.color color like 'FFFFFF', 'rgba(0, 0, 0, 0.5)'
639 - */
640 - setBrush(option) {
641 - const drawingMode = this._drawingMode;
642 - let compName = components.FREE_DRAWING;
643 -
644 - if (drawingMode === drawingModes.LINE_DRAWING) {
645 - compName = components.LINE;
646 - }
647 -
648 - this.getComponent(compName).setBrush(option);
649 - }
650 -
651 - /**
652 - * Set states of current drawing shape
653 - * @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle')
654 - * @param {Object} [options] - Shape options
655 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
656 - * Shape foreground color (ex: '#fff', 'transparent')
657 - * @param {string} [options.stoke] - Shape outline color
658 - * @param {number} [options.strokeWidth] - Shape outline width
659 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
660 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
661 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
662 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
663 - * @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
664 - */
665 - setDrawingShape(type, options) {
666 - this.getComponent(components.SHAPE).setStates(type, options);
667 - }
668 -
669 - /**
670 - * Set style of current drawing icon
671 - * @param {string} type - icon type (ex: 'icon-arrow', 'icon-star')
672 - * @param {Object} [iconColor] - Icon color
673 - */
674 - setIconStyle(type, iconColor) {
675 - this.getComponent(components.ICON).setStates(type, iconColor);
676 - }
677 -
678 - /**
679 - * Register icon paths
680 - * @param {Object} pathInfos - Path infos
681 - * @param {string} pathInfos.key - key
682 - * @param {string} pathInfos.value - value
683 - */
684 - registerPaths(pathInfos) {
685 - this.getComponent(components.ICON).registerPaths(pathInfos);
686 - }
687 -
688 - /**
689 - * Change cursor style
690 - * @param {string} cursorType - cursor type
691 - */
692 - changeCursor(cursorType) {
693 - const canvas = this.getCanvas();
694 - canvas.defaultCursor = cursorType;
695 - canvas.renderAll();
696 - }
697 -
698 - /**
699 - * Whether it has the filter or not
700 - * @param {string} type - Filter type
701 - * @returns {boolean} true if it has the filter
702 - */
703 - hasFilter(type) {
704 - return this.getComponent(components.FILTER).hasFilter(type);
705 - }
706 -
707 - /**
708 - * Set selection style of fabric object by init option
709 - * @param {Object} styles - Selection styles
710 - */
711 - setSelectionStyle(styles) {
712 - extend(fObjectOptions.SELECTION_STYLE, styles);
713 - }
714 -
715 - /**
716 - * Set object properties
717 - * @param {number} id - object id
718 - * @param {Object} props - props
719 - * @param {string} [props.fill] Color
720 - * @param {string} [props.fontFamily] Font type for text
721 - * @param {number} [props.fontSize] Size
722 - * @param {string} [props.fontStyle] Type of inclination (normal / italic)
723 - * @param {string} [props.fontWeight] Type of thicker or thinner looking (normal / bold)
724 - * @param {string} [props.textAlign] Type of text align (left / center / right)
725 - * @param {string} [props.textDecoration] Type of line (underline / line-through / overline)
726 - * @returns {Object} applied properties
727 - */
728 - setObjectProperties(id, props) {
729 - const object = this.getObject(id);
730 - const clone = extend({}, props);
731 -
732 - object.set(clone);
733 -
734 - object.setCoords();
735 -
736 - this.getCanvas().renderAll();
737 -
738 - return clone;
739 - }
740 -
741 - /**
742 - * Get object properties corresponding key
743 - * @param {number} id - object id
744 - * @param {Array<string>|ObjectProps|string} keys - property's key
745 - * @returns {Object} properties
746 - */
747 - getObjectProperties(id, keys) {
748 - const object = this.getObject(id);
749 - const props = {};
750 -
751 - if (isString(keys)) {
752 - props[keys] = object[keys];
753 - } else if (isArray(keys)) {
754 - forEachArray(keys, (value) => {
755 - props[value] = object[value];
756 - });
757 - } else {
758 - forEachOwnProperties(keys, (value, key) => {
759 - props[key] = object[key];
760 - });
761 - }
762 -
763 - return props;
764 - }
765 -
766 - /**
767 - * Get object position by originX, originY
768 - * @param {number} id - object id
769 - * @param {string} originX - can be 'left', 'center', 'right'
770 - * @param {string} originY - can be 'top', 'center', 'bottom'
771 - * @returns {Object} {{x:number, y: number}} position by origin if id is valid, or null
772 - */
773 - getObjectPosition(id, originX, originY) {
774 - const targetObj = this.getObject(id);
775 - if (!targetObj) {
776 - return null;
777 - }
778 -
779 - return targetObj.getPointByOrigin(originX, originY);
780 - }
781 -
782 - /**
783 - * Set object position by originX, originY
784 - * @param {number} id - object id
785 - * @param {Object} posInfo - position object
786 - * @param {number} posInfo.x - x position
787 - * @param {number} posInfo.y - y position
788 - * @param {string} posInfo.originX - can be 'left', 'center', 'right'
789 - * @param {string} posInfo.originY - can be 'top', 'center', 'bottom'
790 - * @returns {boolean} true if target id is valid or false
791 - */
792 - setObjectPosition(id, posInfo) {
793 - const targetObj = this.getObject(id);
794 - const { x, y, originX, originY } = posInfo;
795 - if (!targetObj) {
796 - return false;
797 - }
798 -
799 - const targetOrigin = targetObj.getPointByOrigin(originX, originY);
800 - const centerOrigin = targetObj.getPointByOrigin('center', 'center');
801 - const diffX = centerOrigin.x - targetOrigin.x;
802 - const diffY = centerOrigin.y - targetOrigin.y;
803 -
804 - targetObj.set({
805 - left: x + diffX,
806 - top: y + diffY,
807 - });
808 -
809 - targetObj.setCoords();
810 -
811 - return true;
812 - }
813 -
814 - /**
815 - * Get the canvas size
816 - * @returns {Object} {{width: number, height: number}} image size
817 - */
818 - getCanvasSize() {
819 - const image = this.getCanvasImage();
820 -
821 - return {
822 - width: image ? image.width : 0,
823 - height: image ? image.height : 0,
824 - };
825 - }
826 -
827 - /**
828 - * Create fabric static canvas
829 - * @returns {Object} {{width: number, height: number}} image size
830 - */
831 - createStaticCanvas() {
832 - const staticCanvas = new fabric.StaticCanvas();
833 -
834 - staticCanvas.set({
835 - enableRetinaScaling: false,
836 - });
837 -
838 - return staticCanvas;
839 - }
840 -
841 - /**
842 - * Get a DrawingMode instance
843 - * @param {string} modeName - DrawingMode Class Name
844 - * @returns {DrawingMode} DrawingMode instance
845 - * @private
846 - */
847 - _getDrawingModeInstance(modeName) {
848 - return this._drawingModeMap[modeName];
849 - }
850 -
851 - /**
852 - * Set object caching to false. This brought many bugs when draw Shape & cropzone
853 - * @see http://fabricjs.com/fabric-object-caching
854 - * @private
855 - */
856 - _setObjectCachingToFalse() {
857 - fabric.Object.prototype.objectCaching = false;
858 - }
859 -
860 - /**
861 - * Set canvas element to fabric.Canvas
862 - * @param {Element|string} element - Wrapper or canvas element or selector
863 - * @private
864 - */
865 - _setCanvasElement(element) {
866 - let selectedElement;
867 - let canvasElement;
868 -
869 - if (element.nodeType) {
870 - selectedElement = element;
871 - } else {
872 - selectedElement = document.querySelector(element);
873 - }
874 -
875 - if (selectedElement.nodeName.toUpperCase() !== 'CANVAS') {
876 - canvasElement = document.createElement('canvas');
877 - selectedElement.appendChild(canvasElement);
878 - }
879 -
880 - this._canvas = new fabric.Canvas(canvasElement, {
881 - containerClass: 'tui-image-editor-canvas-container',
882 - enableRetinaScaling: false,
883 - });
884 - }
885 -
886 - /**
887 - * Creates DrawingMode instances
888 - * @private
889 - */
890 - _createDrawingModeInstances() {
891 - this._register(this._drawingModeMap, new CropperDrawingMode());
892 - this._register(this._drawingModeMap, new FreeDrawingMode());
893 - this._register(this._drawingModeMap, new LineDrawingMode());
894 - this._register(this._drawingModeMap, new ShapeDrawingMode());
895 - this._register(this._drawingModeMap, new TextDrawingMode());
896 - this._register(this._drawingModeMap, new IconDrawingMode());
897 - }
898 -
899 - /**
900 - * Create components
901 - * @private
902 - */
903 - _createComponents() {
904 - this._register(this._componentMap, new ImageLoader(this));
905 - this._register(this._componentMap, new Cropper(this));
906 - this._register(this._componentMap, new Flip(this));
907 - this._register(this._componentMap, new Rotation(this));
908 - this._register(this._componentMap, new FreeDrawing(this));
909 - this._register(this._componentMap, new Line(this));
910 - this._register(this._componentMap, new Text(this));
911 - this._register(this._componentMap, new Icon(this));
912 - this._register(this._componentMap, new Filter(this));
913 - this._register(this._componentMap, new Shape(this));
914 - }
915 -
916 - /**
917 - * Register component
918 - * @param {Object} map - map object
919 - * @param {Object} module - module which has getName method
920 - * @private
921 - */
922 - _register(map, module) {
923 - map[module.getName()] = module;
924 - }
925 -
926 - /**
927 - * Get the current drawing mode is same with given mode
928 - * @param {string} mode drawing mode
929 - * @returns {boolean} true if same or false
930 - */
931 - _isSameDrawingMode(mode) {
932 - return this.getDrawingMode() === mode;
933 - }
934 -
935 - /**
936 - * Calculate max dimension of canvas
937 - * The css-max dimension is dynamically decided with maintaining image ratio
938 - * The css-max dimension is lower than canvas dimension (attribute of canvas, not css)
939 - * @param {number} width - Canvas width
940 - * @param {number} height - Canvas height
941 - * @returns {{width: number, height: number}} - Max width & Max height
942 - * @private
943 - */
944 - _calcMaxDimension(width, height) {
945 - const wScaleFactor = this.cssMaxWidth / width;
946 - const hScaleFactor = this.cssMaxHeight / height;
947 - let cssMaxWidth = Math.min(width, this.cssMaxWidth);
948 - let cssMaxHeight = Math.min(height, this.cssMaxHeight);
949 -
950 - if (wScaleFactor < 1 && wScaleFactor < hScaleFactor) {
951 - cssMaxWidth = width * wScaleFactor;
952 - cssMaxHeight = height * wScaleFactor;
953 - } else if (hScaleFactor < 1 && hScaleFactor < wScaleFactor) {
954 - cssMaxWidth = width * hScaleFactor;
955 - cssMaxHeight = height * hScaleFactor;
956 - }
957 -
958 - return {
959 - width: Math.floor(cssMaxWidth),
960 - height: Math.floor(cssMaxHeight),
961 - };
962 - }
963 -
964 - /**
965 - * Callback function after loading image
966 - * @param {fabric.Image} obj - Fabric image object
967 - * @private
968 - */
969 - _callbackAfterLoadingImageObject(obj) {
970 - const centerPos = this.getCanvasImage().getCenterPoint();
971 -
972 - obj.set(fObjectOptions.SELECTION_STYLE);
973 - obj.set({
974 - left: centerPos.x,
975 - top: centerPos.y,
976 - crossOrigin: 'Anonymous',
977 - });
978 -
979 - this.getCanvas().add(obj).setActiveObject(obj);
980 - }
981 -
982 - /**
983 - * Attach canvas's events
984 - */
985 - _attachCanvasEvents() {
986 - const canvas = this._canvas;
987 - const handler = this._handler;
988 - canvas.on({
989 - 'mouse:down': handler.onMouseDown,
990 - 'object:added': handler.onObjectAdded,
991 - 'object:removed': handler.onObjectRemoved,
992 - 'object:moving': handler.onObjectMoved,
993 - 'object:scaling': handler.onObjectScaled,
994 - 'object:modified': handler.onObjectModified,
995 - 'object:rotating': handler.onObjectRotated,
996 - 'path:created': handler.onPathCreated,
997 - 'selection:cleared': handler.onSelectionCleared,
998 - 'selection:created': handler.onSelectionCreated,
999 - 'selection:updated': handler.onObjectSelected,
1000 - });
1001 - }
1002 -
1003 - /**
1004 - * "mouse:down" canvas event handler
1005 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1006 - * @private
1007 - */
1008 - _onMouseDown(fEvent) {
1009 - const { e: event, target } = fEvent;
1010 - const originPointer = this._canvas.getPointer(event);
1011 -
1012 - if (target) {
1013 - const { type } = target;
1014 - const undoData = makeSelectionUndoData(target, (item) =>
1015 - makeSelectionUndoDatum(this.getObjectId(item), item, type === 'activeSelection')
1016 - );
1017 -
1018 - setCachedUndoDataForDimension(undoData);
1019 - }
1020 -
1021 - this.fire(events.MOUSE_DOWN, event, originPointer);
1022 - }
1023 -
1024 - /**
1025 - * "object:added" canvas event handler
1026 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1027 - * @private
1028 - */
1029 - _onObjectAdded(fEvent) {
1030 - const obj = fEvent.target;
1031 - if (obj.isType('cropzone')) {
1032 - return;
1033 - }
1034 -
1035 - this._addFabricObject(obj);
1036 - }
1037 -
1038 - /**
1039 - * "object:removed" canvas event handler
1040 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1041 - * @private
1042 - */
1043 - _onObjectRemoved(fEvent) {
1044 - const obj = fEvent.target;
1045 -
1046 - this._removeFabricObject(stamp(obj));
1047 - }
1048 -
1049 - /**
1050 - * "object:moving" canvas event handler
1051 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1052 - * @private
1053 - */
1054 - _onObjectMoved(fEvent) {
1055 - this._lazyFire(
1056 - events.OBJECT_MOVED,
1057 - (object) => this.createObjectProperties(object),
1058 - fEvent.target
1059 - );
1060 - }
1061 -
1062 - /**
1063 - * "object:scaling" canvas event handler
1064 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1065 - * @private
1066 - */
1067 - _onObjectScaled(fEvent) {
1068 - this._lazyFire(
1069 - events.OBJECT_SCALED,
1070 - (object) => this.createObjectProperties(object),
1071 - fEvent.target
1072 - );
1073 - }
1074 -
1075 - /**
1076 - * "object:modified" canvas event handler
1077 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1078 - * @private
1079 - */
1080 - _onObjectModified(fEvent) {
1081 - const { target } = fEvent;
1082 - if (target.type === 'activeSelection') {
1083 - const items = target.getObjects();
1084 -
1085 - items.forEach((item) => item.fire('modifiedInGroup', target));
1086 - }
1087 -
1088 - this.fire(events.OBJECT_MODIFIED, target, this.getObjectId(target));
1089 - }
1090 -
1091 - /**
1092 - * "object:rotating" canvas event handler
1093 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1094 - * @private
1095 - */
1096 - _onObjectRotated(fEvent) {
1097 - this._lazyFire(
1098 - events.OBJECT_ROTATED,
1099 - (object) => this.createObjectProperties(object),
1100 - fEvent.target
1101 - );
1102 - }
1103 -
1104 - /**
1105 - * Lazy event emitter
1106 - * @param {string} eventName - event name
1107 - * @param {Function} paramsMaker - make param function
1108 - * @param {Object} [target] - Object of the event owner.
1109 - * @private
1110 - */
1111 - _lazyFire(eventName, paramsMaker, target) {
1112 - const existEventDelegation = target && target.canvasEventDelegation;
1113 - const delegationState = existEventDelegation ? target.canvasEventDelegation(eventName) : 'none';
1114 -
1115 - if (delegationState === 'unregisted') {
1116 - target.canvasEventRegister(eventName, (object) => {
1117 - this.fire(eventName, paramsMaker(object));
1118 - });
1119 - }
1120 -
1121 - if (delegationState === 'none') {
1122 - this.fire(eventName, paramsMaker(target));
1123 - }
1124 - }
1125 -
1126 - /**
1127 - * "object:selected" canvas event handler
1128 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1129 - * @private
1130 - */
1131 - _onObjectSelected(fEvent) {
1132 - const { target } = fEvent;
1133 - const params = this.createObjectProperties(target);
1134 -
1135 - this.fire(events.OBJECT_ACTIVATED, params);
1136 - }
1137 -
1138 - /**
1139 - * "path:created" canvas event handler
1140 - * @param {{path: fabric.Path}} obj - Path object
1141 - * @private
1142 - */
1143 - _onPathCreated(obj) {
1144 - const { x: left, y: top } = obj.path.getCenterPoint();
1145 - obj.path.set(
1146 - extend(
1147 - {
1148 - left,
1149 - top,
1150 - },
1151 - fObjectOptions.SELECTION_STYLE
1152 - )
1153 - );
1154 -
1155 - const params = this.createObjectProperties(obj.path);
1156 -
1157 - this.fire(events.ADD_OBJECT, params);
1158 - }
1159 -
1160 - /**
1161 - * "selction:cleared" canvas event handler
1162 - * @private
1163 - */
1164 - _onSelectionCleared() {
1165 - this.fire(events.SELECTION_CLEARED);
1166 - }
1167 -
1168 - /**
1169 - * "selction:created" canvas event handler
1170 - * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
1171 - * @private
1172 - */
1173 - _onSelectionCreated(fEvent) {
1174 - const { target } = fEvent;
1175 - const params = this.createObjectProperties(target);
1176 -
1177 - this.fire(events.OBJECT_ACTIVATED, params);
1178 - this.fire(events.SELECTION_CREATED, fEvent.target);
1179 - }
1180 -
1181 - /**
1182 - * Canvas discard selection all
1183 - */
1184 - discardSelection() {
1185 - this._canvas.discardActiveObject();
1186 - this._canvas.renderAll();
1187 - }
1188 -
1189 - /**
1190 - * Canvas Selectable status change
1191 - * @param {boolean} selectable - expect status
1192 - */
1193 - changeSelectableAll(selectable) {
1194 - this._canvas.forEachObject((obj) => {
1195 - obj.selectable = selectable;
1196 - obj.hoverCursor = selectable ? 'move' : 'crosshair';
1197 - });
1198 - }
1199 -
1200 - /**
1201 - * Return object's properties
1202 - * @param {fabric.Object} obj - fabric object
1203 - * @returns {Object} properties object
1204 - */
1205 - createObjectProperties(obj) {
1206 - const predefinedKeys = [
1207 - 'left',
1208 - 'top',
1209 - 'width',
1210 - 'height',
1211 - 'fill',
1212 - 'stroke',
1213 - 'strokeWidth',
1214 - 'opacity',
1215 - 'angle',
1216 - ];
1217 - const props = {
1218 - id: stamp(obj),
1219 - type: obj.type,
1220 - };
1221 -
1222 - extend(props, getProperties(obj, predefinedKeys));
1223 -
1224 - if (includes(['i-text', 'text'], obj.type)) {
1225 - extend(props, this._createTextProperties(obj, props));
1226 - } else if (includes(['rect', 'triangle', 'circle'], obj.type)) {
1227 - const shapeComp = this.getComponent(components.SHAPE);
1228 - extend(props, {
1229 - fill: shapeComp.makeFillPropertyForUserEvent(obj),
1230 - });
1231 - }
1232 -
1233 - return props;
1234 - }
1235 -
1236 - /**
1237 - * Get text object's properties
1238 - * @param {fabric.Object} obj - fabric text object
1239 - * @param {Object} props - properties
1240 - * @returns {Object} properties object
1241 - */
1242 - _createTextProperties(obj) {
1243 - const predefinedKeys = [
1244 - 'text',
1245 - 'fontFamily',
1246 - 'fontSize',
1247 - 'fontStyle',
1248 - 'textAlign',
1249 - 'textDecoration',
1250 - 'fontWeight',
1251 - ];
1252 - const props = {};
1253 - extend(props, getProperties(obj, predefinedKeys));
1254 -
1255 - return props;
1256 - }
1257 -
1258 - /**
1259 - * Add object array by id
1260 - * @param {fabric.Object} obj - fabric object
1261 - * @returns {number} object id
1262 - */
1263 - _addFabricObject(obj) {
1264 - const id = stamp(obj);
1265 - this._objects[id] = obj;
1266 -
1267 - return id;
1268 - }
1269 -
1270 - /**
1271 - * Remove an object in array yb id
1272 - * @param {number} id - object id
1273 - */
1274 - _removeFabricObject(id) {
1275 - delete this._objects[id];
1276 - }
1277 -
1278 - /**
1279 - * Reset targetObjectForCopyPaste value from activeObject
1280 - */
1281 - resetTargetObjectForCopyPaste() {
1282 - const activeObject = this.getActiveObject();
1283 -
1284 - if (activeObject) {
1285 - this.targetObjectForCopyPaste = activeObject;
1286 - }
1287 - }
1288 -
1289 - /**
1290 - * Paste fabric object
1291 - * @returns {Promise}
1292 - */
1293 - pasteObject() {
1294 - if (!this.targetObjectForCopyPaste) {
1295 - return Promise.resolve([]);
1296 - }
1297 -
1298 - const targetObject = this.targetObjectForCopyPaste;
1299 - const isGroupSelect = targetObject.type === 'activeSelection';
1300 - const targetObjects = isGroupSelect ? targetObject.getObjects() : [targetObject];
1301 - let newTargetObject = null;
1302 -
1303 - this.discardSelection();
1304 -
1305 - return this._cloneObject(targetObjects).then((addedObjects) => {
1306 - if (addedObjects.length > 1) {
1307 - newTargetObject = this.getActiveSelectionFromObjects(addedObjects);
1308 - } else {
1309 - [newTargetObject] = addedObjects;
1310 - }
1311 - this.targetObjectForCopyPaste = newTargetObject;
1312 - this.setActiveObject(newTargetObject);
1313 - });
1314 - }
1315 -
1316 - /**
1317 - * Clone object
1318 - * @param {fabric.Object} targetObjects - fabric object
1319 - * @returns {Promise}
1320 - * @private
1321 - */
1322 - _cloneObject(targetObjects) {
1323 - const addedObjects = snippet.map(targetObjects, (targetObject) =>
1324 - this._cloneObjectItem(targetObject)
1325 - );
1326 -
1327 - return Promise.all(addedObjects);
1328 - }
1329 -
1330 - /**
1331 - * Clone object one item
1332 - * @param {fabric.Object} targetObject - fabric object
1333 - * @returns {Promise}
1334 - * @private
1335 - */
1336 - _cloneObjectItem(targetObject) {
1337 - return this._copyFabricObjectForPaste(targetObject).then((clonedObject) => {
1338 - const objectProperties = this.createObjectProperties(clonedObject);
1339 - this.add(clonedObject);
1340 -
1341 - this.fire(events.ADD_OBJECT, objectProperties);
1342 -
1343 - return clonedObject;
1344 - });
1345 - }
1346 -
1347 - /**
1348 - * Copy fabric object with Changed position for copy and paste
1349 - * @param {fabric.Object} targetObject - fabric object
1350 - * @returns {Promise}
1351 - * @private
1352 - */
1353 - _copyFabricObjectForPaste(targetObject) {
1354 - const addExtraPx = (value, isReverse) =>
1355 - isReverse ? value - EXTRA_PX_FOR_PASTE : value + EXTRA_PX_FOR_PASTE;
1356 -
1357 - return this._copyFabricObject(targetObject).then((clonedObject) => {
1358 - const { left, top, width, height } = clonedObject;
1359 - const { width: canvasWidth, height: canvasHeight } = this.getCanvasSize();
1360 - const rightEdge = left + width / 2;
1361 - const bottomEdge = top + height / 2;
1362 -
1363 - clonedObject.set(
1364 - snippet.extend(
1365 - {
1366 - left: addExtraPx(left, rightEdge + EXTRA_PX_FOR_PASTE > canvasWidth),
1367 - top: addExtraPx(top, bottomEdge + EXTRA_PX_FOR_PASTE > canvasHeight),
1368 - },
1369 - fObjectOptions.SELECTION_STYLE
1370 - )
1371 - );
1372 -
1373 - return clonedObject;
1374 - });
1375 - }
1376 -
1377 - /**
1378 - * Copy fabric object
1379 - * @param {fabric.Object} targetObject - fabric object
1380 - * @returns {Promise}
1381 - * @private
1382 - */
1383 - _copyFabricObject(targetObject) {
1384 - return new Promise((resolve) => {
1385 - targetObject.clone((cloned) => {
1386 - const shapeComp = this.getComponent(components.SHAPE);
1387 - if (isShape(cloned)) {
1388 - shapeComp.processForCopiedObject(cloned, targetObject);
1389 - }
1390 -
1391 - resolve(cloned);
1392 - });
1393 - });
1394 - }
1395 -}
1396 -
1397 -CustomEvents.mixin(Graphics);
1398 -
1399 -export default Graphics;
1 -/*
2 - imagetracer.js version 1.2.4
3 - Simple raster image tracer and vectorizer written in JavaScript.
4 - andras@jankovics.net
5 -*/
6 -
7 -/*
8 - The Unlicense / PUBLIC DOMAIN
9 - This is free and unencumbered software released into the public domain.
10 - Anyone is free to copy, modify, publish, use, compile, sell, or
11 - distribute this software, either in source code form or as a compiled
12 - binary, for any purpose, commercial or non-commercial, and by any
13 - means.
14 - In jurisdictions that recognize copyright laws, the author or authors
15 - of this software dedicate any and all copyright interest in the
16 - software to the public domain. We make this dedication for the benefit
17 - of the public at large and to the detriment of our heirs and
18 - successors. We intend this dedication to be an overt act of
19 - relinquishment in perpetuity of all present and future rights to this
20 - software under copyright law.
21 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 - OTHER DEALINGS IN THE SOFTWARE.
28 - For more information, please refer to http://unlicense.org/
29 -*/
30 -export default class ImageTracer {
31 - static tracerDefaultOption() {
32 - return {
33 - pathomit: 100,
34 - ltres: 0.1,
35 - qtres: 1,
36 -
37 - scale: 1,
38 - strokewidth: 5,
39 - viewbox: false,
40 - linefilter: true,
41 - desc: false,
42 - rightangleenhance: false,
43 - pal: [
44 - {
45 - r: 0,
46 - g: 0,
47 - b: 0,
48 - a: 255,
49 - },
50 - {
51 - r: 255,
52 - g: 255,
53 - b: 255,
54 - a: 255,
55 - },
56 - ],
57 - };
58 - }
59 - /* eslint-disable */
60 - constructor() {
61 - this.versionnumber = '1.2.4';
62 - this.optionpresets = {
63 - default: {
64 - corsenabled: false,
65 - ltres: 1,
66 - qtres: 1,
67 - pathomit: 8,
68 - rightangleenhance: true,
69 - colorsampling: 2,
70 - numberofcolors: 16,
71 - mincolorratio: 0,
72 - colorquantcycles: 3,
73 - layering: 0,
74 - strokewidth: 1,
75 - linefilter: false,
76 - scale: 1,
77 - roundcoords: 1,
78 - viewbox: false,
79 - desc: false,
80 - lcpr: 0,
81 - qcpr: 0,
82 - blurradius: 0,
83 - blurdelta: 20,
84 - },
85 - posterized1: {
86 - colorsampling: 0,
87 - numberofcolors: 2,
88 - },
89 - posterized2: {
90 - numberofcolors: 4,
91 - blurradius: 5,
92 - },
93 - curvy: {
94 - ltres: 0.01,
95 - linefilter: true,
96 - rightangleenhance: false,
97 - },
98 - sharp: { qtres: 0.01, linefilter: false },
99 - detailed: { pathomit: 0, roundcoords: 2, ltres: 0.5, qtres: 0.5, numberofcolors: 64 },
100 - smoothed: { blurradius: 5, blurdelta: 64 },
101 - grayscale: { colorsampling: 0, colorquantcycles: 1, numberofcolors: 7 },
102 - fixedpalette: { colorsampling: 0, colorquantcycles: 1, numberofcolors: 27 },
103 - randomsampling1: { colorsampling: 1, numberofcolors: 8 },
104 - randomsampling2: { colorsampling: 1, numberofcolors: 64 },
105 - artistic1: {
106 - colorsampling: 0,
107 - colorquantcycles: 1,
108 - pathomit: 0,
109 - blurradius: 5,
110 - blurdelta: 64,
111 - ltres: 0.01,
112 - linefilter: true,
113 - numberofcolors: 16,
114 - strokewidth: 2,
115 - },
116 - artistic2: {
117 - qtres: 0.01,
118 - colorsampling: 0,
119 - colorquantcycles: 1,
120 - numberofcolors: 4,
121 - strokewidth: 0,
122 - },
123 - artistic3: { qtres: 10, ltres: 10, numberofcolors: 8 },
124 - artistic4: {
125 - qtres: 10,
126 - ltres: 10,
127 - numberofcolors: 64,
128 - blurradius: 5,
129 - blurdelta: 256,
130 - strokewidth: 2,
131 - },
132 - posterized3: {
133 - ltres: 1,
134 - qtres: 1,
135 - pathomit: 20,
136 - rightangleenhance: true,
137 - colorsampling: 0,
138 - numberofcolors: 3,
139 - mincolorratio: 0,
140 - colorquantcycles: 3,
141 - blurradius: 3,
142 - blurdelta: 20,
143 - strokewidth: 0,
144 - linefilter: false,
145 - roundcoords: 1,
146 - pal: [
147 - { r: 0, g: 0, b: 100, a: 255 },
148 - { r: 255, g: 255, b: 255, a: 255 },
149 - ],
150 - },
151 - };
152 -
153 - this.pathscan_combined_lookup = [
154 - [
155 - [-1, -1, -1, -1],
156 - [-1, -1, -1, -1],
157 - [-1, -1, -1, -1],
158 - [-1, -1, -1, -1],
159 - ],
160 - [
161 - [0, 1, 0, -1],
162 - [-1, -1, -1, -1],
163 - [-1, -1, -1, -1],
164 - [0, 2, -1, 0],
165 - ],
166 - [
167 - [-1, -1, -1, -1],
168 - [-1, -1, -1, -1],
169 - [0, 1, 0, -1],
170 - [0, 0, 1, 0],
171 - ],
172 - [
173 - [0, 0, 1, 0],
174 - [-1, -1, -1, -1],
175 - [0, 2, -1, 0],
176 - [-1, -1, -1, -1],
177 - ],
178 - [
179 - [-1, -1, -1, -1],
180 - [0, 0, 1, 0],
181 - [0, 3, 0, 1],
182 - [-1, -1, -1, -1],
183 - ],
184 - [
185 - [13, 3, 0, 1],
186 - [13, 2, -1, 0],
187 - [7, 1, 0, -1],
188 - [7, 0, 1, 0],
189 - ],
190 - [
191 - [-1, -1, -1, -1],
192 - [0, 1, 0, -1],
193 - [-1, -1, -1, -1],
194 - [0, 3, 0, 1],
195 - ],
196 - [
197 - [0, 3, 0, 1],
198 - [0, 2, -1, 0],
199 - [-1, -1, -1, -1],
200 - [-1, -1, -1, -1],
201 - ],
202 - [
203 - [0, 3, 0, 1],
204 - [0, 2, -1, 0],
205 - [-1, -1, -1, -1],
206 - [-1, -1, -1, -1],
207 - ],
208 - [
209 - [-1, -1, -1, -1],
210 - [0, 1, 0, -1],
211 - [-1, -1, -1, -1],
212 - [0, 3, 0, 1],
213 - ],
214 - [
215 - [11, 1, 0, -1],
216 - [14, 0, 1, 0],
217 - [14, 3, 0, 1],
218 - [11, 2, -1, 0],
219 - ],
220 - [
221 - [-1, -1, -1, -1],
222 - [0, 0, 1, 0],
223 - [0, 3, 0, 1],
224 - [-1, -1, -1, -1],
225 - ],
226 - [
227 - [0, 0, 1, 0],
228 - [-1, -1, -1, -1],
229 - [0, 2, -1, 0],
230 - [-1, -1, -1, -1],
231 - ],
232 - [
233 - [-1, -1, -1, -1],
234 - [-1, -1, -1, -1],
235 - [0, 1, 0, -1],
236 - [0, 0, 1, 0],
237 - ],
238 - [
239 - [0, 1, 0, -1],
240 - [-1, -1, -1, -1],
241 - [-1, -1, -1, -1],
242 - [0, 2, -1, 0],
243 - ],
244 - [
245 - [-1, -1, -1, -1],
246 - [-1, -1, -1, -1],
247 - [-1, -1, -1, -1],
248 - [-1, -1, -1, -1],
249 - ],
250 - ];
251 -
252 - this.gks = [
253 - [0.27901, 0.44198, 0.27901],
254 - [0.135336, 0.228569, 0.272192, 0.228569, 0.135336],
255 - [0.086776, 0.136394, 0.178908, 0.195843, 0.178908, 0.136394, 0.086776],
256 - [0.063327, 0.093095, 0.122589, 0.144599, 0.152781, 0.144599, 0.122589, 0.093095, 0.063327],
257 - [
258 - 0.049692,
259 - 0.069304,
260 - 0.089767,
261 - 0.107988,
262 - 0.120651,
263 - 0.125194,
264 - 0.120651,
265 - 0.107988,
266 - 0.089767,
267 - 0.069304,
268 - 0.049692,
269 - ],
270 - ];
271 -
272 - this.specpalette = [
273 - { r: 0, g: 0, b: 0, a: 255 },
274 - { r: 128, g: 128, b: 128, a: 255 },
275 - { r: 0, g: 0, b: 128, a: 255 },
276 - { r: 64, g: 64, b: 128, a: 255 },
277 - { r: 192, g: 192, b: 192, a: 255 },
278 - { r: 255, g: 255, b: 255, a: 255 },
279 - { r: 128, g: 128, b: 192, a: 255 },
280 - { r: 0, g: 0, b: 192, a: 255 },
281 - { r: 128, g: 0, b: 0, a: 255 },
282 - { r: 128, g: 64, b: 64, a: 255 },
283 - { r: 128, g: 0, b: 128, a: 255 },
284 - { r: 168, g: 168, b: 168, a: 255 },
285 - { r: 192, g: 128, b: 128, a: 255 },
286 - { r: 192, g: 0, b: 0, a: 255 },
287 - { r: 255, g: 255, b: 255, a: 255 },
288 - { r: 0, g: 128, b: 0, a: 255 },
289 - ];
290 - }
291 -
292 - imageToSVG(url, callback, options) {
293 - options = this.checkoptions(options);
294 - this.loadImage(
295 - url,
296 - (canvas) => {
297 - callback(this.imagedataToSVG(this.getImgdata(canvas), options));
298 - },
299 - options
300 - );
301 - }
302 -
303 - imagedataToSVG(imgd, options) {
304 - options = this.checkoptions(options);
305 - const td = this.imagedataToTracedata(imgd, options);
306 -
307 - return this.getsvgstring(td, options);
308 - }
309 -
310 - imageToTracedata(url, callback, options) {
311 - options = this.checkoptions(options);
312 - this.loadImage(
313 - url,
314 - (canvas) => {
315 - callback(this.imagedataToTracedata(this.getImgdata(canvas), options));
316 - },
317 - options
318 - );
319 - }
320 -
321 - imagedataToTracedata(imgd, options) {
322 - options = this.checkoptions(options);
323 - const ii = this.colorquantization(imgd, options);
324 - let tracedata;
325 - if (options.layering === 0) {
326 - tracedata = {
327 - layers: [],
328 - palette: ii.palette,
329 - width: ii.array[0].length - 2,
330 - height: ii.array.length - 2,
331 - };
332 -
333 - for (let colornum = 0; colornum < ii.palette.length; colornum += 1) {
334 - const tracedlayer = this.batchtracepaths(
335 - this.internodes(
336 - this.pathscan(this.layeringstep(ii, colornum), options.pathomit),
337 - options
338 - ),
339 - options.ltres,
340 - options.qtres
341 - );
342 - tracedata.layers.push(tracedlayer);
343 - }
344 - } else {
345 - const ls = this.layering(ii);
346 - if (options.layercontainerid) {
347 - this.drawLayers(ls, this.specpalette, options.scale, options.layercontainerid);
348 - }
349 - const bps = this.batchpathscan(ls, options.pathomit);
350 - const bis = this.batchinternodes(bps, options);
351 - tracedata = {
352 - layers: this.batchtracelayers(bis, options.ltres, options.qtres),
353 - palette: ii.palette,
354 - width: imgd.width,
355 - height: imgd.height,
356 - };
357 - }
358 -
359 - return tracedata;
360 - }
361 -
362 - checkoptions(options) {
363 - options = options || {};
364 - if (typeof options === 'string') {
365 - options = options.toLowerCase();
366 - if (this.optionpresets[options]) {
367 - options = this.optionpresets[options];
368 - } else {
369 - options = {};
370 - }
371 - }
372 - const ok = Object.keys(this.optionpresets['default']);
373 - for (let k = 0; k < ok.length; k += 1) {
374 - if (!options.hasOwnProperty(ok[k])) {
375 - options[ok[k]] = this.optionpresets['default'][ok[k]];
376 - }
377 - }
378 -
379 - return options;
380 - }
381 -
382 - colorquantization(imgd, options) {
383 - const arr = [];
384 - let idx = 0;
385 - let cd;
386 - let cdl;
387 - let ci;
388 - const paletteacc = [];
389 - const pixelnum = imgd.width * imgd.height;
390 - let i;
391 - let j;
392 - let k;
393 - let cnt;
394 - let palette;
395 -
396 - for (j = 0; j < imgd.height + 2; j += 1) {
397 - arr[j] = [];
398 - for (i = 0; i < imgd.width + 2; i += 1) {
399 - arr[j][i] = -1;
400 - }
401 - }
402 - if (options.pal) {
403 - palette = options.pal;
404 - } else if (options.colorsampling === 0) {
405 - palette = this.generatepalette(options.numberofcolors);
406 - } else if (options.colorsampling === 1) {
407 - palette = this.samplepalette(options.numberofcolors, imgd);
408 - } else {
409 - palette = this.samplepalette2(options.numberofcolors, imgd);
410 - }
411 - if (options.blurradius > 0) {
412 - imgd = this.blur(imgd, options.blurradius, options.blurdelta);
413 - }
414 - for (cnt = 0; cnt < options.colorquantcycles; cnt += 1) {
415 - if (cnt > 0) {
416 - for (k = 0; k < palette.length; k += 1) {
417 - if (paletteacc[k].n > 0) {
418 - palette[k] = {
419 - r: Math.floor(paletteacc[k].r / paletteacc[k].n),
420 - g: Math.floor(paletteacc[k].g / paletteacc[k].n),
421 - b: Math.floor(paletteacc[k].b / paletteacc[k].n),
422 - a: Math.floor(paletteacc[k].a / paletteacc[k].n),
423 - };
424 - }
425 -
426 - if (
427 - paletteacc[k].n / pixelnum < options.mincolorratio &&
428 - cnt < options.colorquantcycles - 1
429 - ) {
430 - palette[k] = {
431 - r: Math.floor(Math.random() * 255),
432 - g: Math.floor(Math.random() * 255),
433 - b: Math.floor(Math.random() * 255),
434 - a: Math.floor(Math.random() * 255),
435 - };
436 - }
437 - }
438 - }
439 -
440 - for (i = 0; i < palette.length; i += 1) {
441 - paletteacc[i] = { r: 0, g: 0, b: 0, a: 0, n: 0 };
442 - }
443 -
444 - for (j = 0; j < imgd.height; j += 1) {
445 - for (i = 0; i < imgd.width; i += 1) {
446 - idx = (j * imgd.width + i) * 4;
447 -
448 - ci = 0;
449 - cdl = 1024;
450 - for (k = 0; k < palette.length; k += 1) {
451 - cd =
452 - Math.abs(palette[k].r - imgd.data[idx]) +
453 - Math.abs(palette[k].g - imgd.data[idx + 1]) +
454 - Math.abs(palette[k].b - imgd.data[idx + 2]) +
455 - Math.abs(palette[k].a - imgd.data[idx + 3]);
456 -
457 - if (cd < cdl) {
458 - cdl = cd;
459 - ci = k;
460 - }
461 - }
462 -
463 - paletteacc[ci].r += imgd.data[idx];
464 - paletteacc[ci].g += imgd.data[idx + 1];
465 - paletteacc[ci].b += imgd.data[idx + 2];
466 - paletteacc[ci].a += imgd.data[idx + 3];
467 - paletteacc[ci].n += 1;
468 -
469 - arr[j + 1][i + 1] = ci;
470 - }
471 - }
472 - }
473 -
474 - return { array: arr, palette };
475 - }
476 -
477 - samplepalette(numberofcolors, imgd) {
478 - let idx;
479 - const palette = [];
480 - for (let i = 0; i < numberofcolors; i += 1) {
481 - idx = Math.floor((Math.random() * imgd.data.length) / 4) * 4;
482 - palette.push({
483 - r: imgd.data[idx],
484 - g: imgd.data[idx + 1],
485 - b: imgd.data[idx + 2],
486 - a: imgd.data[idx + 3],
487 - });
488 - }
489 -
490 - return palette;
491 - }
492 -
493 - samplepalette2(numberofcolors, imgd) {
494 - let idx;
495 - const palette = [];
496 - const ni = Math.ceil(Math.sqrt(numberofcolors));
497 - const nj = Math.ceil(numberofcolors / ni);
498 - const vx = imgd.width / (ni + 1);
499 - const vy = imgd.height / (nj + 1);
500 - for (let j = 0; j < nj; j += 1) {
501 - for (let i = 0; i < ni; i += 1) {
502 - if (palette.length === numberofcolors) {
503 - break;
504 - } else {
505 - idx = Math.floor((j + 1) * vy * imgd.width + (i + 1) * vx) * 4;
506 - palette.push({
507 - r: imgd.data[idx],
508 - g: imgd.data[idx + 1],
509 - b: imgd.data[idx + 2],
510 - a: imgd.data[idx + 3],
511 - });
512 - }
513 - }
514 - }
515 -
516 - return palette;
517 - }
518 -
519 - generatepalette(numberofcolors) {
520 - const palette = [];
521 - let rcnt;
522 - let gcnt;
523 - let bcnt;
524 - if (numberofcolors < 8) {
525 - const graystep = Math.floor(255 / (numberofcolors - 1));
526 - for (let i = 0; i < numberofcolors; i += 1) {
527 - palette.push({ r: i * graystep, g: i * graystep, b: i * graystep, a: 255 });
528 - }
529 - } else {
530 - const colorqnum = Math.floor(Math.pow(numberofcolors, 1 / 3));
531 - const colorstep = Math.floor(255 / (colorqnum - 1));
532 - const rndnum = numberofcolors - colorqnum * colorqnum * colorqnum;
533 - for (rcnt = 0; rcnt < colorqnum; rcnt += 1) {
534 - for (gcnt = 0; gcnt < colorqnum; gcnt += 1) {
535 - for (bcnt = 0; bcnt < colorqnum; bcnt += 1) {
536 - palette.push({ r: rcnt * colorstep, g: gcnt * colorstep, b: bcnt * colorstep, a: 255 });
537 - }
538 - }
539 - }
540 - for (rcnt = 0; rcnt < rndnum; rcnt += 1) {
541 - palette.push({
542 - r: Math.floor(Math.random() * 255),
543 - g: Math.floor(Math.random() * 255),
544 - b: Math.floor(Math.random() * 255),
545 - a: Math.floor(Math.random() * 255),
546 - });
547 - }
548 - }
549 -
550 - return palette;
551 - }
552 -
553 - layering(ii) {
554 - const layers = [];
555 - let val = 0;
556 - const ah = ii.array.length;
557 - const aw = ii.array[0].length;
558 - let n1;
559 - let n2;
560 - let n3;
561 - let n4;
562 - let n5;
563 - let n6;
564 - let n7;
565 - let n8;
566 - let i;
567 - let j;
568 - let k;
569 - for (k = 0; k < ii.palette.length; k += 1) {
570 - layers[k] = [];
571 - for (j = 0; j < ah; j += 1) {
572 - layers[k][j] = [];
573 - for (i = 0; i < aw; i += 1) {
574 - layers[k][j][i] = 0;
575 - }
576 - }
577 - }
578 - for (j = 1; j < ah - 1; j += 1) {
579 - for (i = 1; i < aw - 1; i += 1) {
580 - val = ii.array[j][i];
581 -
582 - n1 = ii.array[j - 1][i - 1] === val ? 1 : 0;
583 - n2 = ii.array[j - 1][i] === val ? 1 : 0;
584 - n3 = ii.array[j - 1][i + 1] === val ? 1 : 0;
585 - n4 = ii.array[j][i - 1] === val ? 1 : 0;
586 - n5 = ii.array[j][i + 1] === val ? 1 : 0;
587 - n6 = ii.array[j + 1][i - 1] === val ? 1 : 0;
588 - n7 = ii.array[j + 1][i] === val ? 1 : 0;
589 - n8 = ii.array[j + 1][i + 1] === val ? 1 : 0;
590 -
591 - layers[val][j + 1][i + 1] = 1 + n5 * 2 + n8 * 4 + n7 * 8;
592 - if (!n4) {
593 - layers[val][j + 1][i] = 0 + 2 + n7 * 4 + n6 * 8;
594 - }
595 - if (!n2) {
596 - layers[val][j][i + 1] = 0 + n3 * 2 + n5 * 4 + 8;
597 - }
598 - if (!n1) {
599 - layers[val][j][i] = 0 + n2 * 2 + 4 + n4 * 8;
600 - }
601 - }
602 - }
603 -
604 - return layers;
605 - }
606 -
607 - layeringstep(ii, cnum) {
608 - const layer = [];
609 - const ah = ii.array.length;
610 - const aw = ii.array[0].length;
611 - let i;
612 - let j;
613 - for (j = 0; j < ah; j += 1) {
614 - layer[j] = [];
615 - for (i = 0; i < aw; i += 1) {
616 - layer[j][i] = 0;
617 - }
618 - }
619 - for (j = 1; j < ah; j += 1) {
620 - for (i = 1; i < aw; i += 1) {
621 - layer[j][i] =
622 - (ii.array[j - 1][i - 1] === cnum ? 1 : 0) +
623 - (ii.array[j - 1][i] === cnum ? 2 : 0) +
624 - (ii.array[j][i - 1] === cnum ? 8 : 0) +
625 - (ii.array[j][i] === cnum ? 4 : 0);
626 - }
627 - }
628 -
629 - return layer;
630 - }
631 -
632 - pathscan(arr, pathomit) {
633 - const paths = [];
634 - let pacnt = 0;
635 - let pcnt = 0;
636 - let px = 0;
637 - let py = 0;
638 - const w = arr[0].length;
639 - const h = arr.length;
640 - let dir = 0;
641 - let pathfinished = true;
642 - let holepath = false;
643 - let lookuprow;
644 - for (let j = 0; j < h; j += 1) {
645 - for (let i = 0; i < w; i += 1) {
646 - if (arr[j][i] === 4 || arr[j][i] === 11) {
647 - px = i;
648 - py = j;
649 - paths[pacnt] = {};
650 - paths[pacnt].points = [];
651 - paths[pacnt].boundingbox = [px, py, px, py];
652 - paths[pacnt].holechildren = [];
653 - pathfinished = false;
654 - pcnt = 0;
655 - holepath = arr[j][i] === 11;
656 - dir = 1;
657 -
658 - while (!pathfinished) {
659 - paths[pacnt].points[pcnt] = {};
660 - paths[pacnt].points[pcnt].x = px - 1;
661 - paths[pacnt].points[pcnt].y = py - 1;
662 - paths[pacnt].points[pcnt].t = arr[py][px];
663 -
664 - if (px - 1 < paths[pacnt].boundingbox[0]) {
665 - paths[pacnt].boundingbox[0] = px - 1;
666 - }
667 - if (px - 1 > paths[pacnt].boundingbox[2]) {
668 - paths[pacnt].boundingbox[2] = px - 1;
669 - }
670 - if (py - 1 < paths[pacnt].boundingbox[1]) {
671 - paths[pacnt].boundingbox[1] = py - 1;
672 - }
673 - if (py - 1 > paths[pacnt].boundingbox[3]) {
674 - paths[pacnt].boundingbox[3] = py - 1;
675 - }
676 -
677 - lookuprow = this.pathscan_combined_lookup[arr[py][px]][dir];
678 - arr[py][px] = lookuprow[0];
679 - dir = lookuprow[1];
680 - px += lookuprow[2];
681 - py += lookuprow[3];
682 -
683 - if (px - 1 === paths[pacnt].points[0].x && py - 1 === paths[pacnt].points[0].y) {
684 - pathfinished = true;
685 -
686 - if (paths[pacnt].points.length < pathomit) {
687 - paths.pop();
688 - } else {
689 - paths[pacnt].isholepath = !!holepath;
690 -
691 - if (holepath) {
692 - let parentidx = 0,
693 - parentbbox = [-1, -1, w + 1, h + 1];
694 - for (let parentcnt = 0; parentcnt < pacnt; parentcnt++) {
695 - if (
696 - !paths[parentcnt].isholepath &&
697 - this.boundingboxincludes(
698 - paths[parentcnt].boundingbox,
699 - paths[pacnt].boundingbox
700 - ) &&
701 - this.boundingboxincludes(parentbbox, paths[parentcnt].boundingbox)
702 - ) {
703 - parentidx = parentcnt;
704 - parentbbox = paths[parentcnt].boundingbox;
705 - }
706 - }
707 - paths[parentidx].holechildren.push(pacnt);
708 - }
709 - pacnt += 1;
710 - }
711 - }
712 - pcnt += 1;
713 - }
714 - }
715 - }
716 - }
717 -
718 - return paths;
719 - }
720 -
721 - boundingboxincludes(parentbbox, childbbox) {
722 - return (
723 - parentbbox[0] < childbbox[0] &&
724 - parentbbox[1] < childbbox[1] &&
725 - parentbbox[2] > childbbox[2] &&
726 - parentbbox[3] > childbbox[3]
727 - );
728 - }
729 -
730 - batchpathscan(layers, pathomit) {
731 - const bpaths = [];
732 - for (const k in layers) {
733 - if (!layers.hasOwnProperty(k)) {
734 - continue;
735 - }
736 - bpaths[k] = this.pathscan(layers[k], pathomit);
737 - }
738 -
739 - return bpaths;
740 - }
741 -
742 - internodes(paths, options) {
743 - const ins = [];
744 - let palen = 0;
745 - let nextidx = 0;
746 - let nextidx2 = 0;
747 - let previdx = 0;
748 - let previdx2 = 0;
749 - let pacnt;
750 - let pcnt;
751 - for (pacnt = 0; pacnt < paths.length; pacnt += 1) {
752 - ins[pacnt] = {};
753 - ins[pacnt].points = [];
754 - ins[pacnt].boundingbox = paths[pacnt].boundingbox;
755 - ins[pacnt].holechildren = paths[pacnt].holechildren;
756 - ins[pacnt].isholepath = paths[pacnt].isholepath;
757 - palen = paths[pacnt].points.length;
758 -
759 - for (pcnt = 0; pcnt < palen; pcnt += 1) {
760 - nextidx = (pcnt + 1) % palen;
761 - nextidx2 = (pcnt + 2) % palen;
762 - previdx = (pcnt - 1 + palen) % palen;
763 - previdx2 = (pcnt - 2 + palen) % palen;
764 -
765 - if (
766 - options.rightangleenhance &&
767 - this.testrightangle(paths[pacnt], previdx2, previdx, pcnt, nextidx, nextidx2)
768 - ) {
769 - if (ins[pacnt].points.length > 0) {
770 - ins[pacnt].points[ins[pacnt].points.length - 1].linesegment = this.getdirection(
771 - ins[pacnt].points[ins[pacnt].points.length - 1].x,
772 - ins[pacnt].points[ins[pacnt].points.length - 1].y,
773 - paths[pacnt].points[pcnt].x,
774 - paths[pacnt].points[pcnt].y
775 - );
776 - }
777 -
778 - ins[pacnt].points.push({
779 - x: paths[pacnt].points[pcnt].x,
780 - y: paths[pacnt].points[pcnt].y,
781 - linesegment: this.getdirection(
782 - paths[pacnt].points[pcnt].x,
783 - paths[pacnt].points[pcnt].y,
784 - (paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2,
785 - (paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2
786 - ),
787 - });
788 - }
789 -
790 - ins[pacnt].points.push({
791 - x: (paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2,
792 - y: (paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2,
793 - linesegment: this.getdirection(
794 - (paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2,
795 - (paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2,
796 - (paths[pacnt].points[nextidx].x + paths[pacnt].points[nextidx2].x) / 2,
797 - (paths[pacnt].points[nextidx].y + paths[pacnt].points[nextidx2].y) / 2
798 - ),
799 - });
800 - }
801 - }
802 -
803 - return ins;
804 - }
805 -
806 - testrightangle(path, idx1, idx2, idx3, idx4, idx5) {
807 - return (
808 - (path.points[idx3].x === path.points[idx1].x &&
809 - path.points[idx3].x === path.points[idx2].x &&
810 - path.points[idx3].y === path.points[idx4].y &&
811 - path.points[idx3].y === path.points[idx5].y) ||
812 - (path.points[idx3].y === path.points[idx1].y &&
813 - path.points[idx3].y === path.points[idx2].y &&
814 - path.points[idx3].x === path.points[idx4].x &&
815 - path.points[idx3].x === path.points[idx5].x)
816 - );
817 - }
818 -
819 - getdirection(x1, y1, x2, y2) {
820 - let val = 8;
821 - if (x1 < x2) {
822 - if (y1 < y2) {
823 - val = 1;
824 - } else if (y1 > y2) {
825 - val = 7;
826 - } else {
827 - val = 0;
828 - }
829 - } else if (x1 > x2) {
830 - if (y1 < y2) {
831 - val = 3;
832 - } else if (y1 > y2) {
833 - val = 5;
834 - } else {
835 - val = 4;
836 - }
837 - } else if (y1 < y2) {
838 - val = 2;
839 - } else if (y1 > y2) {
840 - val = 6;
841 - } else {
842 - val = 8;
843 - }
844 -
845 - return val;
846 - }
847 -
848 - batchinternodes(bpaths, options) {
849 - const binternodes = [];
850 - for (const k in bpaths) {
851 - if (!bpaths.hasOwnProperty(k)) {
852 - continue;
853 - }
854 - binternodes[k] = this.internodes(bpaths[k], options);
855 - }
856 -
857 - return binternodes;
858 - }
859 -
860 - tracepath(path, ltres, qtres) {
861 - let pcnt = 0;
862 - let segtype1;
863 - let segtype2;
864 - let seqend;
865 - const smp = {};
866 - smp.segments = [];
867 - smp.boundingbox = path.boundingbox;
868 - smp.holechildren = path.holechildren;
869 - smp.isholepath = path.isholepath;
870 -
871 - while (pcnt < path.points.length) {
872 - segtype1 = path.points[pcnt].linesegment;
873 - segtype2 = -1;
874 - seqend = pcnt + 1;
875 - while (
876 - (path.points[seqend].linesegment === segtype1 ||
877 - path.points[seqend].linesegment === segtype2 ||
878 - segtype2 === -1) &&
879 - seqend < path.points.length - 1
880 - ) {
881 - if (path.points[seqend].linesegment !== segtype1 && segtype2 === -1) {
882 - segtype2 = path.points[seqend].linesegment;
883 - }
884 - seqend += 1;
885 - }
886 - if (seqend === path.points.length - 1) {
887 - seqend = 0;
888 - }
889 -
890 - smp.segments = smp.segments.concat(this.fitseq(path, ltres, qtres, pcnt, seqend));
891 -
892 - if (seqend > 0) {
893 - pcnt = seqend;
894 - } else {
895 - pcnt = path.points.length;
896 - }
897 - }
898 -
899 - return smp;
900 - }
901 -
902 - fitseq(path, ltres, qtres, seqstart, seqend) {
903 - if (seqend > path.points.length || seqend < 0) {
904 - return [];
905 - }
906 - let errorpoint = seqstart,
907 - errorval = 0,
908 - curvepass = true,
909 - px,
910 - py,
911 - dist2;
912 - let tl = seqend - seqstart;
913 - if (tl < 0) {
914 - tl += path.points.length;
915 - }
916 - let vx = (path.points[seqend].x - path.points[seqstart].x) / tl,
917 - vy = (path.points[seqend].y - path.points[seqstart].y) / tl;
918 - let pcnt = (seqstart + 1) % path.points.length,
919 - pl;
920 - while (pcnt != seqend) {
921 - pl = pcnt - seqstart;
922 - if (pl < 0) {
923 - pl += path.points.length;
924 - }
925 - px = path.points[seqstart].x + vx * pl;
926 - py = path.points[seqstart].y + vy * pl;
927 - dist2 =
928 - (path.points[pcnt].x - px) * (path.points[pcnt].x - px) +
929 - (path.points[pcnt].y - py) * (path.points[pcnt].y - py);
930 - if (dist2 > ltres) {
931 - curvepass = false;
932 - }
933 - if (dist2 > errorval) {
934 - errorpoint = pcnt;
935 - errorval = dist2;
936 - }
937 - pcnt = (pcnt + 1) % path.points.length;
938 - }
939 - if (curvepass) {
940 - return [
941 - {
942 - type: 'L',
943 - x1: path.points[seqstart].x,
944 - y1: path.points[seqstart].y,
945 - x2: path.points[seqend].x,
946 - y2: path.points[seqend].y,
947 - },
948 - ];
949 - }
950 - const fitpoint = errorpoint;
951 - curvepass = true;
952 - errorval = 0;
953 - let t = (fitpoint - seqstart) / tl,
954 - t1 = (1 - t) * (1 - t),
955 - t2 = 2 * (1 - t) * t,
956 - t3 = t * t;
957 - let cpx =
958 - (t1 * path.points[seqstart].x + t3 * path.points[seqend].x - path.points[fitpoint].x) / -t2,
959 - cpy =
960 - (t1 * path.points[seqstart].y + t3 * path.points[seqend].y - path.points[fitpoint].y) / -t2;
961 - pcnt = seqstart + 1;
962 - while (pcnt != seqend) {
963 - t = (pcnt - seqstart) / tl;
964 - t1 = (1 - t) * (1 - t);
965 - t2 = 2 * (1 - t) * t;
966 - t3 = t * t;
967 - px = t1 * path.points[seqstart].x + t2 * cpx + t3 * path.points[seqend].x;
968 - py = t1 * path.points[seqstart].y + t2 * cpy + t3 * path.points[seqend].y;
969 - dist2 =
970 - (path.points[pcnt].x - px) * (path.points[pcnt].x - px) +
971 - (path.points[pcnt].y - py) * (path.points[pcnt].y - py);
972 - if (dist2 > qtres) {
973 - curvepass = false;
974 - }
975 - if (dist2 > errorval) {
976 - errorpoint = pcnt;
977 - errorval = dist2;
978 - }
979 - pcnt = (pcnt + 1) % path.points.length;
980 - }
981 - if (curvepass) {
982 - return [
983 - {
984 - type: 'Q',
985 - x1: path.points[seqstart].x,
986 - y1: path.points[seqstart].y,
987 - x2: cpx,
988 - y2: cpy,
989 - x3: path.points[seqend].x,
990 - y3: path.points[seqend].y,
991 - },
992 - ];
993 - }
994 - const splitpoint = fitpoint;
995 -
996 - return this.fitseq(path, ltres, qtres, seqstart, splitpoint).concat(
997 - this.fitseq(path, ltres, qtres, splitpoint, seqend)
998 - );
999 - }
1000 -
1001 - batchtracepaths(internodepaths, ltres, qtres) {
1002 - const btracedpaths = [];
1003 - for (const k in internodepaths) {
1004 - if (!internodepaths.hasOwnProperty(k)) {
1005 - continue;
1006 - }
1007 - btracedpaths.push(this.tracepath(internodepaths[k], ltres, qtres));
1008 - }
1009 -
1010 - return btracedpaths;
1011 - }
1012 -
1013 - batchtracelayers(binternodes, ltres, qtres) {
1014 - const btbis = [];
1015 - for (const k in binternodes) {
1016 - if (!binternodes.hasOwnProperty(k)) {
1017 - continue;
1018 - }
1019 - btbis[k] = this.batchtracepaths(binternodes[k], ltres, qtres);
1020 - }
1021 -
1022 - return btbis;
1023 - }
1024 -
1025 - roundtodec(val, places) {
1026 - return Number(val.toFixed(places));
1027 - }
1028 -
1029 - svgpathstring(tracedata, lnum, pathnum, options) {
1030 - let layer = tracedata.layers[lnum],
1031 - smp = layer[pathnum],
1032 - str = '',
1033 - pcnt;
1034 - if (options.linefilter && smp.segments.length < 3) {
1035 - return str;
1036 - }
1037 - str = `<path ${options.desc ? `desc="l ${lnum} p ${pathnum}" ` : ''}${this.tosvgcolorstr(
1038 - tracedata.palette[lnum],
1039 - options
1040 - )}d="`;
1041 - if (options.roundcoords === -1) {
1042 - str += `M ${smp.segments[0].x1 * options.scale} ${smp.segments[0].y1 * options.scale} `;
1043 - for (pcnt = 0; pcnt < smp.segments.length; pcnt++) {
1044 - str += `${smp.segments[pcnt].type} ${smp.segments[pcnt].x2 * options.scale} ${
1045 - smp.segments[pcnt].y2 * options.scale
1046 - } `;
1047 - if (smp.segments[pcnt].hasOwnProperty('x3')) {
1048 - str += `${smp.segments[pcnt].x3 * options.scale} ${
1049 - smp.segments[pcnt].y3 * options.scale
1050 - } `;
1051 - }
1052 - }
1053 - str += 'Z ';
1054 - } else {
1055 - str += `M ${this.roundtodec(
1056 - smp.segments[0].x1 * options.scale,
1057 - options.roundcoords
1058 - )} ${this.roundtodec(smp.segments[0].y1 * options.scale, options.roundcoords)} `;
1059 - for (pcnt = 0; pcnt < smp.segments.length; pcnt++) {
1060 - str += `${smp.segments[pcnt].type} ${this.roundtodec(
1061 - smp.segments[pcnt].x2 * options.scale,
1062 - options.roundcoords
1063 - )} ${this.roundtodec(smp.segments[pcnt].y2 * options.scale, options.roundcoords)} `;
1064 - if (smp.segments[pcnt].hasOwnProperty('x3')) {
1065 - str += `${this.roundtodec(
1066 - smp.segments[pcnt].x3 * options.scale,
1067 - options.roundcoords
1068 - )} ${this.roundtodec(smp.segments[pcnt].y3 * options.scale, options.roundcoords)} `;
1069 - }
1070 - }
1071 - str += 'Z ';
1072 - }
1073 - for (var hcnt = 0; hcnt < smp.holechildren.length; hcnt++) {
1074 - var hsmp = layer[smp.holechildren[hcnt]];
1075 -
1076 - if (options.roundcoords === -1) {
1077 - if (hsmp.segments[hsmp.segments.length - 1].hasOwnProperty('x3')) {
1078 - str += `M ${hsmp.segments[hsmp.segments.length - 1].x3 * options.scale} ${
1079 - hsmp.segments[hsmp.segments.length - 1].y3 * options.scale
1080 - } `;
1081 - } else {
1082 - str += `M ${hsmp.segments[hsmp.segments.length - 1].x2 * options.scale} ${
1083 - hsmp.segments[hsmp.segments.length - 1].y2 * options.scale
1084 - } `;
1085 - }
1086 - for (pcnt = hsmp.segments.length - 1; pcnt >= 0; pcnt--) {
1087 - str += `${hsmp.segments[pcnt].type} `;
1088 - if (hsmp.segments[pcnt].hasOwnProperty('x3')) {
1089 - str += `${hsmp.segments[pcnt].x2 * options.scale} ${
1090 - hsmp.segments[pcnt].y2 * options.scale
1091 - } `;
1092 - }
1093 - str += `${hsmp.segments[pcnt].x1 * options.scale} ${
1094 - hsmp.segments[pcnt].y1 * options.scale
1095 - } `;
1096 - }
1097 - } else {
1098 - if (hsmp.segments[hsmp.segments.length - 1].hasOwnProperty('x3')) {
1099 - str += `M ${this.roundtodec(
1100 - hsmp.segments[hsmp.segments.length - 1].x3 * options.scale
1101 - )} ${this.roundtodec(hsmp.segments[hsmp.segments.length - 1].y3 * options.scale)} `;
1102 - } else {
1103 - str += `M ${this.roundtodec(
1104 - hsmp.segments[hsmp.segments.length - 1].x2 * options.scale
1105 - )} ${this.roundtodec(hsmp.segments[hsmp.segments.length - 1].y2 * options.scale)} `;
1106 - }
1107 - for (pcnt = hsmp.segments.length - 1; pcnt >= 0; pcnt--) {
1108 - str += `${hsmp.segments[pcnt].type} `;
1109 - if (hsmp.segments[pcnt].hasOwnProperty('x3')) {
1110 - str += `${this.roundtodec(hsmp.segments[pcnt].x2 * options.scale)} ${this.roundtodec(
1111 - hsmp.segments[pcnt].y2 * options.scale
1112 - )} `;
1113 - }
1114 - str += `${this.roundtodec(hsmp.segments[pcnt].x1 * options.scale)} ${this.roundtodec(
1115 - hsmp.segments[pcnt].y1 * options.scale
1116 - )} `;
1117 - }
1118 - }
1119 - str += 'Z ';
1120 - }
1121 - str += '" />';
1122 - if (options.lcpr || options.qcpr) {
1123 - for (pcnt = 0; pcnt < smp.segments.length; pcnt++) {
1124 - if (smp.segments[pcnt].hasOwnProperty('x3') && options.qcpr) {
1125 - str += `<circle cx="${smp.segments[pcnt].x2 * options.scale}" cy="${
1126 - smp.segments[pcnt].y2 * options.scale
1127 - }" r="${options.qcpr}" fill="cyan" stroke-width="${
1128 - options.qcpr * 0.2
1129 - }" stroke="black" />`;
1130 - str += `<circle cx="${smp.segments[pcnt].x3 * options.scale}" cy="${
1131 - smp.segments[pcnt].y3 * options.scale
1132 - }" r="${options.qcpr}" fill="white" stroke-width="${
1133 - options.qcpr * 0.2
1134 - }" stroke="black" />`;
1135 - str += `<line x1="${smp.segments[pcnt].x1 * options.scale}" y1="${
1136 - smp.segments[pcnt].y1 * options.scale
1137 - }" x2="${smp.segments[pcnt].x2 * options.scale}" y2="${
1138 - smp.segments[pcnt].y2 * options.scale
1139 - }" stroke-width="${options.qcpr * 0.2}" stroke="cyan" />`;
1140 - str += `<line x1="${smp.segments[pcnt].x2 * options.scale}" y1="${
1141 - smp.segments[pcnt].y2 * options.scale
1142 - }" x2="${smp.segments[pcnt].x3 * options.scale}" y2="${
1143 - smp.segments[pcnt].y3 * options.scale
1144 - }" stroke-width="${options.qcpr * 0.2}" stroke="cyan" />`;
1145 - }
1146 - if (!smp.segments[pcnt].hasOwnProperty('x3') && options.lcpr) {
1147 - str += `<circle cx="${smp.segments[pcnt].x2 * options.scale}" cy="${
1148 - smp.segments[pcnt].y2 * options.scale
1149 - }" r="${options.lcpr}" fill="white" stroke-width="${
1150 - options.lcpr * 0.2
1151 - }" stroke="black" />`;
1152 - }
1153 - }
1154 -
1155 - for (var hcnt = 0; hcnt < smp.holechildren.length; hcnt++) {
1156 - var hsmp = layer[smp.holechildren[hcnt]];
1157 - for (pcnt = 0; pcnt < hsmp.segments.length; pcnt++) {
1158 - if (hsmp.segments[pcnt].hasOwnProperty('x3') && options.qcpr) {
1159 - str += `<circle cx="${hsmp.segments[pcnt].x2 * options.scale}" cy="${
1160 - hsmp.segments[pcnt].y2 * options.scale
1161 - }" r="${options.qcpr}" fill="cyan" stroke-width="${
1162 - options.qcpr * 0.2
1163 - }" stroke="black" />`;
1164 - str += `<circle cx="${hsmp.segments[pcnt].x3 * options.scale}" cy="${
1165 - hsmp.segments[pcnt].y3 * options.scale
1166 - }" r="${options.qcpr}" fill="white" stroke-width="${
1167 - options.qcpr * 0.2
1168 - }" stroke="black" />`;
1169 - str += `<line x1="${hsmp.segments[pcnt].x1 * options.scale}" y1="${
1170 - hsmp.segments[pcnt].y1 * options.scale
1171 - }" x2="${hsmp.segments[pcnt].x2 * options.scale}" y2="${
1172 - hsmp.segments[pcnt].y2 * options.scale
1173 - }" stroke-width="${options.qcpr * 0.2}" stroke="cyan" />`;
1174 - str += `<line x1="${hsmp.segments[pcnt].x2 * options.scale}" y1="${
1175 - hsmp.segments[pcnt].y2 * options.scale
1176 - }" x2="${hsmp.segments[pcnt].x3 * options.scale}" y2="${
1177 - hsmp.segments[pcnt].y3 * options.scale
1178 - }" stroke-width="${options.qcpr * 0.2}" stroke="cyan" />`;
1179 - }
1180 - if (!hsmp.segments[pcnt].hasOwnProperty('x3') && options.lcpr) {
1181 - str += `<circle cx="${hsmp.segments[pcnt].x2 * options.scale}" cy="${
1182 - hsmp.segments[pcnt].y2 * options.scale
1183 - }" r="${options.lcpr}" fill="white" stroke-width="${
1184 - options.lcpr * 0.2
1185 - }" stroke="black" />`;
1186 - }
1187 - }
1188 - }
1189 - }
1190 -
1191 - return str;
1192 - }
1193 -
1194 - getsvgstring(tracedata, options) {
1195 - options = this.checkoptions(options);
1196 - const w = tracedata.width * options.scale;
1197 - const h = tracedata.height * options.scale;
1198 -
1199 - let svgstr = `<svg ${
1200 - options.viewbox ? `viewBox="0 0 ${w} ${h}" ` : `width="${w}" height="${h}" `
1201 - }version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created with imagetracer.js version ${
1202 - this.versionnumber
1203 - }" >`;
1204 - for (let lcnt = 0; lcnt < tracedata.layers.length; lcnt += 1) {
1205 - for (let pcnt = 0; pcnt < tracedata.layers[lcnt].length; pcnt += 1) {
1206 - if (!tracedata.layers[lcnt][pcnt].isholepath) {
1207 - svgstr += this.svgpathstring(tracedata, lcnt, pcnt, options);
1208 - }
1209 - }
1210 - }
1211 - svgstr += '</svg>';
1212 -
1213 - return svgstr;
1214 - }
1215 -
1216 - compareNumbers(a, b) {
1217 - return a - b;
1218 - }
1219 -
1220 - torgbastr(c) {
1221 - return `rgba(${c.r},${c.g},${c.b},${c.a})`;
1222 - }
1223 -
1224 - tosvgcolorstr(c, options) {
1225 - return `fill="rgb(${c.r},${c.g},${c.b})" stroke="rgb(${c.r},${c.g},${c.b})" stroke-width="${
1226 - options.strokewidth
1227 - }" opacity="${c.a / 255.0}" `;
1228 - }
1229 -
1230 - appendSVGString(svgstr, parentid) {
1231 - let div;
1232 - if (parentid) {
1233 - div = document.getElementById(parentid);
1234 - if (!div) {
1235 - div = document.createElement('div');
1236 - div.id = parentid;
1237 - document.body.appendChild(div);
1238 - }
1239 - } else {
1240 - div = document.createElement('div');
1241 - document.body.appendChild(div);
1242 - }
1243 - div.innerHTML += svgstr;
1244 - }
1245 -
1246 - blur(imgd, radius, delta) {
1247 - let i, j, k, d, idx, racc, gacc, bacc, aacc, wacc;
1248 - const imgd2 = { width: imgd.width, height: imgd.height, data: [] };
1249 - radius = Math.floor(radius);
1250 - if (radius < 1) {
1251 - return imgd;
1252 - }
1253 - if (radius > 5) {
1254 - radius = 5;
1255 - }
1256 - delta = Math.abs(delta);
1257 - if (delta > 1024) {
1258 - delta = 1024;
1259 - }
1260 - const thisgk = this.gks[radius - 1];
1261 - for (j = 0; j < imgd.height; j++) {
1262 - for (i = 0; i < imgd.width; i++) {
1263 - racc = 0;
1264 - gacc = 0;
1265 - bacc = 0;
1266 - aacc = 0;
1267 - wacc = 0;
1268 -
1269 - for (k = -radius; k < radius + 1; k++) {
1270 - if (i + k > 0 && i + k < imgd.width) {
1271 - idx = (j * imgd.width + i + k) * 4;
1272 - racc += imgd.data[idx] * thisgk[k + radius];
1273 - gacc += imgd.data[idx + 1] * thisgk[k + radius];
1274 - bacc += imgd.data[idx + 2] * thisgk[k + radius];
1275 - aacc += imgd.data[idx + 3] * thisgk[k + radius];
1276 - wacc += thisgk[k + radius];
1277 - }
1278 - }
1279 -
1280 - idx = (j * imgd.width + i) * 4;
1281 - imgd2.data[idx] = Math.floor(racc / wacc);
1282 - imgd2.data[idx + 1] = Math.floor(gacc / wacc);
1283 - imgd2.data[idx + 2] = Math.floor(bacc / wacc);
1284 - imgd2.data[idx + 3] = Math.floor(aacc / wacc);
1285 - }
1286 - }
1287 - const himgd = new Uint8ClampedArray(imgd2.data);
1288 - for (j = 0; j < imgd.height; j++) {
1289 - for (i = 0; i < imgd.width; i++) {
1290 - racc = 0;
1291 - gacc = 0;
1292 - bacc = 0;
1293 - aacc = 0;
1294 - wacc = 0;
1295 -
1296 - for (k = -radius; k < radius + 1; k++) {
1297 - if (j + k > 0 && j + k < imgd.height) {
1298 - idx = ((j + k) * imgd.width + i) * 4;
1299 - racc += himgd[idx] * thisgk[k + radius];
1300 - gacc += himgd[idx + 1] * thisgk[k + radius];
1301 - bacc += himgd[idx + 2] * thisgk[k + radius];
1302 - aacc += himgd[idx + 3] * thisgk[k + radius];
1303 - wacc += thisgk[k + radius];
1304 - }
1305 - }
1306 -
1307 - idx = (j * imgd.width + i) * 4;
1308 - imgd2.data[idx] = Math.floor(racc / wacc);
1309 - imgd2.data[idx + 1] = Math.floor(gacc / wacc);
1310 - imgd2.data[idx + 2] = Math.floor(bacc / wacc);
1311 - imgd2.data[idx + 3] = Math.floor(aacc / wacc);
1312 - }
1313 - }
1314 - for (j = 0; j < imgd.height; j++) {
1315 - for (i = 0; i < imgd.width; i++) {
1316 - idx = (j * imgd.width + i) * 4;
1317 -
1318 - d =
1319 - Math.abs(imgd2.data[idx] - imgd.data[idx]) +
1320 - Math.abs(imgd2.data[idx + 1] - imgd.data[idx + 1]) +
1321 - Math.abs(imgd2.data[idx + 2] - imgd.data[idx + 2]) +
1322 - Math.abs(imgd2.data[idx + 3] - imgd.data[idx + 3]);
1323 -
1324 - if (d > delta) {
1325 - imgd2.data[idx] = imgd.data[idx];
1326 - imgd2.data[idx + 1] = imgd.data[idx + 1];
1327 - imgd2.data[idx + 2] = imgd.data[idx + 2];
1328 - imgd2.data[idx + 3] = imgd.data[idx + 3];
1329 - }
1330 - }
1331 - }
1332 -
1333 - return imgd2;
1334 - }
1335 -
1336 - loadImage(url, callback, options) {
1337 - const img = new Image();
1338 - if (options && options.corsenabled) {
1339 - img.crossOrigin = 'Anonymous';
1340 - }
1341 - img.src = url;
1342 - img.onload = function () {
1343 - const canvas = document.createElement('canvas');
1344 - canvas.width = img.width;
1345 - canvas.height = img.height;
1346 - const context = canvas.getContext('2d');
1347 - context.drawImage(img, 0, 0);
1348 - callback(canvas);
1349 - };
1350 - }
1351 -
1352 - getImgdata(canvas) {
1353 - const context = canvas.getContext('2d');
1354 -
1355 - return context.getImageData(0, 0, canvas.width, canvas.height);
1356 - }
1357 -
1358 - drawLayers(layers, palette, scale, parentid) {
1359 - scale = scale || 1;
1360 - let w, h, i, j, k;
1361 - let div;
1362 - if (parentid) {
1363 - div = document.getElementById(parentid);
1364 - if (!div) {
1365 - div = document.createElement('div');
1366 - div.id = parentid;
1367 - document.body.appendChild(div);
1368 - }
1369 - } else {
1370 - div = document.createElement('div');
1371 - document.body.appendChild(div);
1372 - }
1373 - for (k in layers) {
1374 - if (!layers.hasOwnProperty(k)) {
1375 - continue;
1376 - }
1377 -
1378 - w = layers[k][0].length;
1379 - h = layers[k].length;
1380 -
1381 - const canvas = document.createElement('canvas');
1382 - canvas.width = w * scale;
1383 - canvas.height = h * scale;
1384 - const context = canvas.getContext('2d');
1385 -
1386 - for (j = 0; j < h; j += 1) {
1387 - for (i = 0; i < w; i += 1) {
1388 - context.fillStyle = this.torgbastr(palette[layers[k][j][i] % palette.length]);
1389 - context.fillRect(i * scale, j * scale, scale, scale);
1390 - }
1391 - }
1392 -
1393 - div.appendChild(canvas);
1394 - }
1395 - }
1396 -}
1 -/**
2 - * @author NHN. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Selection modification helper
4 - */
5 -
6 -import { extend } from 'tui-code-snippet/src/js/object';
7 -
8 -/**
9 - * Cached selection's info
10 - * @type {Array}
11 - * @private
12 - */
13 -let cachedUndoDataForChangeDimension = null;
14 -
15 -/**
16 - * Set cached undo data
17 - * @param {Array} undoData - selection object
18 - * @private
19 - */
20 -export function setCachedUndoDataForDimension(undoData) {
21 - cachedUndoDataForChangeDimension = undoData;
22 -}
23 -
24 -/**
25 - * Get cached undo data
26 - * @returns {Object} cached undo data
27 - * @private
28 - */
29 -export function getCachedUndoDataForDimension() {
30 - return cachedUndoDataForChangeDimension;
31 -}
32 -
33 -/**
34 - * Make undo data
35 - * @param {fabric.Object} obj - selection object
36 - * @param {Function} undoDatumMaker - make undo datum
37 - * @returns {Array} undoData
38 - * @private
39 - */
40 -export function makeSelectionUndoData(obj, undoDatumMaker) {
41 - let undoData;
42 -
43 - if (obj.type === 'activeSelection') {
44 - undoData = obj.getObjects().map((item) => {
45 - const { angle, left, top, scaleX, scaleY, width, height } = item;
46 -
47 - obj.realizeTransform(item);
48 - const result = undoDatumMaker(item);
49 -
50 - item.set({
51 - angle,
52 - left,
53 - top,
54 - width,
55 - height,
56 - scaleX,
57 - scaleY,
58 - });
59 -
60 - return result;
61 - });
62 - } else {
63 - undoData = [undoDatumMaker(obj)];
64 - }
65 -
66 - return undoData;
67 -}
68 -
69 -/**
70 - * Make undo datum
71 - * @param {number} id - object id
72 - * @param {fabric.Object} obj - selection object
73 - * @param {boolean} isSelection - whether or not object is selection
74 - * @returns {Object} undo datum
75 - * @private
76 - */
77 -export function makeSelectionUndoDatum(id, obj, isSelection) {
78 - return isSelection
79 - ? {
80 - id,
81 - width: obj.width,
82 - height: obj.height,
83 - top: obj.top,
84 - left: obj.left,
85 - angle: obj.angle,
86 - scaleX: obj.scaleX,
87 - scaleY: obj.scaleY,
88 - }
89 - : extend({ id }, obj);
90 -}
1 -/**
2 - * @author NHN. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Shape resize helper
4 - */
5 -import { forEach, map, extend } from 'tui-code-snippet';
6 -import { capitalizeString, flipObject, setCustomProperty, getCustomProperty } from '../util';
7 -import resizeHelper from '../helper/shapeResizeHelper';
8 -
9 -const FILTER_OPTION_MAP = {
10 - pixelate: 'blocksize',
11 - blur: 'blur',
12 -};
13 -const POSITION_DIMENSION_MAP = {
14 - x: 'width',
15 - y: 'height',
16 -};
17 -
18 -const FILTER_NAME_VALUE_MAP = flipObject(FILTER_OPTION_MAP);
19 -
20 -/**
21 - * Cached canvas image element for fill image
22 - * @type {boolean}
23 - * @private
24 - */
25 -let cachedCanvasImageElement = null;
26 -
27 -/**
28 - * Get background image of fill
29 - * @param {fabric.Object} shapeObj - Shape object
30 - * @returns {fabric.Image}
31 - * @private
32 - */
33 -export function getFillImageFromShape(shapeObj) {
34 - const { patternSourceCanvas } = getCustomProperty(shapeObj, 'patternSourceCanvas');
35 - const [fillImage] = patternSourceCanvas.getObjects();
36 -
37 - return fillImage;
38 -}
39 -
40 -/**
41 - * Reset the image position in the filter type fill area.
42 - * @param {fabric.Object} shapeObj - Shape object
43 - * @private
44 - */
45 -export function rePositionFilterTypeFillImage(shapeObj) {
46 - const { angle, flipX, flipY } = shapeObj;
47 - const fillImage = getFillImageFromShape(shapeObj);
48 - const rotatedShapeCornerDimension = getRotatedDimension(shapeObj);
49 - const { right, bottom } = rotatedShapeCornerDimension;
50 - let { width, height } = rotatedShapeCornerDimension;
51 - const diffLeft = (width - shapeObj.width) / 2;
52 - const diffTop = (height - shapeObj.height) / 2;
53 - const cropX = shapeObj.left - shapeObj.width / 2 - diffLeft;
54 - const cropY = shapeObj.top - shapeObj.height / 2 - diffTop;
55 - let left = width / 2 - diffLeft;
56 - let top = height / 2 - diffTop;
57 - const fillImageMaxSize = Math.max(width, height) + Math.max(diffLeft, diffTop);
58 -
59 - [left, top, width, height] = calculateFillImageDimensionOutsideCanvas({
60 - shapeObj,
61 - left,
62 - top,
63 - width,
64 - height,
65 - cropX,
66 - cropY,
67 - flipX,
68 - flipY,
69 - right,
70 - bottom,
71 - });
72 -
73 - fillImage.set({
74 - angle: flipX === flipY ? -angle : angle,
75 - left,
76 - top,
77 - width,
78 - height,
79 - cropX,
80 - cropY,
81 - flipX,
82 - flipY,
83 - });
84 -
85 - setCustomProperty(fillImage, { fillImageMaxSize });
86 -}
87 -
88 -/**
89 - * Make filter option from fabric image
90 - * @param {fabric.Image} imageObject - fabric image object
91 - * @returns {object}
92 - */
93 -export function makeFilterOptionFromFabricImage(imageObject) {
94 - return map(imageObject.filters, (filter) => {
95 - const [key] = Object.keys(filter);
96 -
97 - return {
98 - [FILTER_NAME_VALUE_MAP[key]]: filter[key],
99 - };
100 - });
101 -}
102 -
103 -/**
104 - * Calculate fill image position and size for out of Canvas
105 - * @param {Object} options - options for position dimension calculate
106 - * @param {fabric.Object} shapeObj - shape object
107 - * @param {number} left - original left position
108 - * @param {number} top - original top position
109 - * @param {number} width - image width
110 - * @param {number} height - image height
111 - * @param {number} cropX - image cropX
112 - * @param {number} cropY - image cropY
113 - * @param {boolean} flipX - shape flipX
114 - * @param {boolean} flipY - shape flipY
115 - * @returns {Object}
116 - */
117 -function calculateFillImageDimensionOutsideCanvas({
118 - shapeObj,
119 - left,
120 - top,
121 - width,
122 - height,
123 - cropX,
124 - cropY,
125 - flipX,
126 - flipY,
127 - right,
128 - bottom,
129 -}) {
130 - const overflowAreaPositionFixer = (type, outDistance, imageLeft, imageTop) =>
131 - calculateDistanceOverflowPart({
132 - type,
133 - outDistance,
134 - shapeObj,
135 - flipX,
136 - flipY,
137 - left: imageLeft,
138 - top: imageTop,
139 - });
140 - const [originalWidth, originalHeight] = [width, height];
141 -
142 - [left, top, width, height] = calculateDimensionLeftTopEdge(overflowAreaPositionFixer, {
143 - left,
144 - top,
145 - width,
146 - height,
147 - cropX,
148 - cropY,
149 - });
150 -
151 - [left, top, width, height] = calculateDimensionRightBottomEdge(overflowAreaPositionFixer, {
152 - left,
153 - top,
154 - insideCanvasRealImageWidth: width,
155 - insideCanvasRealImageHeight: height,
156 - right,
157 - bottom,
158 - cropX,
159 - cropY,
160 - originalWidth,
161 - originalHeight,
162 - });
163 -
164 - return [left, top, width, height];
165 -}
166 -
167 -/**
168 - * Calculate fill image position and size for for right bottom edge
169 - * @param {Function} overflowAreaPositionFixer - position fixer
170 - * @param {Object} options - options for position dimension calculate
171 - * @param {fabric.Object} shapeObj - shape object
172 - * @param {number} left - original left position
173 - * @param {number} top - original top position
174 - * @param {number} width - image width
175 - * @param {number} height - image height
176 - * @param {number} right - image right
177 - * @param {number} bottom - image bottom
178 - * @param {number} cropX - image cropX
179 - * @param {number} cropY - image cropY
180 - * @param {boolean} originalWidth - image original width
181 - * @param {boolean} originalHeight - image original height
182 - * @returns {Object}
183 - */
184 -function calculateDimensionRightBottomEdge(
185 - overflowAreaPositionFixer,
186 - {
187 - left,
188 - top,
189 - insideCanvasRealImageWidth,
190 - insideCanvasRealImageHeight,
191 - right,
192 - bottom,
193 - cropX,
194 - cropY,
195 - originalWidth,
196 - originalHeight,
197 - }
198 -) {
199 - let [width, height] = [insideCanvasRealImageWidth, insideCanvasRealImageHeight];
200 - const { width: canvasWidth, height: canvasHeight } = cachedCanvasImageElement;
201 -
202 - if (right > canvasWidth && cropX > 0) {
203 - width = originalWidth - Math.abs(right - canvasWidth);
204 - }
205 - if (bottom > canvasHeight && cropY > 0) {
206 - height = originalHeight - Math.abs(bottom - canvasHeight);
207 - }
208 -
209 - const diff = {
210 - x: (insideCanvasRealImageWidth - width) / 2,
211 - y: (insideCanvasRealImageHeight - height) / 2,
212 - };
213 -
214 - forEach(['x', 'y'], (type) => {
215 - const cropDistance2 = diff[type];
216 - if (cropDistance2 > 0) {
217 - [left, top] = overflowAreaPositionFixer(type, cropDistance2, left, top);
218 - }
219 - });
220 -
221 - return [left, top, width, height];
222 -}
223 -
224 -/**
225 - * Calculate fill image position and size for for left top
226 - * @param {Function} overflowAreaPositionFixer - position fixer
227 - * @param {Object} options - options for position dimension calculate
228 - * @param {fabric.Object} shapeObj - shape object
229 - * @param {number} left - original left position
230 - * @param {number} top - original top position
231 - * @param {number} width - image width
232 - * @param {number} height - image height
233 - * @param {number} cropX - image cropX
234 - * @param {number} cropY - image cropY
235 - * @returns {Object}
236 - */
237 -function calculateDimensionLeftTopEdge(
238 - overflowAreaPositionFixer,
239 - { left, top, width, height, cropX, cropY }
240 -) {
241 - const dimension = {
242 - width,
243 - height,
244 - };
245 -
246 - forEach(['x', 'y'], (type) => {
247 - const cropDistance = type === 'x' ? cropX : cropY;
248 - const compareSize = dimension[POSITION_DIMENSION_MAP[type]];
249 - const standardSize = cachedCanvasImageElement[POSITION_DIMENSION_MAP[type]];
250 -
251 - if (compareSize > standardSize) {
252 - const outDistance = (compareSize - standardSize) / 2;
253 -
254 - dimension[POSITION_DIMENSION_MAP[type]] = standardSize;
255 - [left, top] = overflowAreaPositionFixer(type, outDistance, left, top);
256 - }
257 - if (cropDistance < 0) {
258 - [left, top] = overflowAreaPositionFixer(type, cropDistance, left, top);
259 - }
260 - });
261 -
262 - return [left, top, dimension.width, dimension.height];
263 -}
264 -
265 -/**
266 - * Make fill property of dynamic pattern type
267 - * @param {fabric.Image} canvasImage - canvas background image
268 - * @param {Array} filterOption - filter option
269 - * @param {fabric.StaticCanvas} patternSourceCanvas - fabric static canvas
270 - * @returns {Object}
271 - */
272 -export function makeFillPatternForFilter(canvasImage, filterOption, patternSourceCanvas) {
273 - const copiedCanvasElement = getCachedCanvasImageElement(canvasImage);
274 - const fillImage = makeFillImage(copiedCanvasElement, canvasImage.angle, filterOption);
275 - patternSourceCanvas.add(fillImage);
276 -
277 - const fabricProperty = {
278 - fill: new fabric.Pattern({
279 - source: patternSourceCanvas.getElement(),
280 - repeat: 'no-repeat',
281 - }),
282 - };
283 -
284 - setCustomProperty(fabricProperty, { patternSourceCanvas });
285 -
286 - return fabricProperty;
287 -}
288 -
289 -/**
290 - * Reset fill pattern canvas
291 - * @param {fabric.StaticCanvas} patternSourceCanvas - fabric static canvas
292 - */
293 -export function resetFillPatternCanvas(patternSourceCanvas) {
294 - const [innerImage] = patternSourceCanvas.getObjects();
295 - let { fillImageMaxSize } = getCustomProperty(innerImage, 'fillImageMaxSize');
296 - fillImageMaxSize = Math.max(1, fillImageMaxSize);
297 -
298 - patternSourceCanvas.setDimensions({
299 - width: fillImageMaxSize,
300 - height: fillImageMaxSize,
301 - });
302 - patternSourceCanvas.renderAll();
303 -}
304 -
305 -/**
306 - * Remake filter pattern image source
307 - * @param {fabric.Object} shapeObj - Shape object
308 - * @param {fabric.Image} canvasImage - canvas background image
309 - */
310 -export function reMakePatternImageSource(shapeObj, canvasImage) {
311 - const { patternSourceCanvas } = getCustomProperty(shapeObj, 'patternSourceCanvas');
312 - const [fillImage] = patternSourceCanvas.getObjects();
313 - const filterOption = makeFilterOptionFromFabricImage(fillImage);
314 -
315 - patternSourceCanvas.remove(fillImage);
316 -
317 - const copiedCanvasElement = getCachedCanvasImageElement(canvasImage, true);
318 - const newFillImage = makeFillImage(copiedCanvasElement, canvasImage.angle, filterOption);
319 -
320 - patternSourceCanvas.add(newFillImage);
321 -}
322 -
323 -/**
324 - * Calculate a point line outside the canvas.
325 - * @param {fabric.Image} canvasImage - canvas background image
326 - * @param {boolean} reset - default is false
327 - * @returns {HTMLImageElement}
328 - */
329 -export function getCachedCanvasImageElement(canvasImage, reset = false) {
330 - if (!cachedCanvasImageElement || reset) {
331 - cachedCanvasImageElement = canvasImage.toCanvasElement();
332 - }
333 -
334 - return cachedCanvasImageElement;
335 -}
336 -
337 -/**
338 - * Calculate fill image position for out of Canvas
339 - * @param {string} type - 'x' or 'y'
340 - * @param {fabric.Object} shapeObj - shape object
341 - * @param {number} outDistance - distance away
342 - * @param {number} left - original left position
343 - * @param {number} top - original top position
344 - * @returns {Array}
345 - */
346 -function calculateDistanceOverflowPart({ type, shapeObj, outDistance, left, top, flipX, flipY }) {
347 - const shapePointNavigation = getShapeEdgePoint(shapeObj);
348 - const shapeNeighborPointNavigation = [
349 - [1, 2],
350 - [0, 3],
351 - [0, 3],
352 - [1, 2],
353 - ];
354 - const linePointsOutsideCanvas = calculateLinePointsOutsideCanvas(
355 - type,
356 - shapePointNavigation,
357 - shapeNeighborPointNavigation
358 - );
359 - const reatAngles = calculateLineAngleOfOutsideCanvas(
360 - type,
361 - shapePointNavigation,
362 - linePointsOutsideCanvas
363 - );
364 - const { startPointIndex } = linePointsOutsideCanvas;
365 - const diffPosition = getReversePositionForFlip({
366 - outDistance,
367 - startPointIndex,
368 - flipX,
369 - flipY,
370 - reatAngles,
371 - });
372 -
373 - return [left + diffPosition.left, top + diffPosition.top];
374 -}
375 -
376 -/**
377 - * Calculate fill image position for out of Canvas
378 - * @param {number} outDistance - distance away
379 - * @param {boolean} flipX - flip x statux
380 - * @param {boolean} flipY - flip y statux
381 - * @param {Array} reatAngles - Line angle of the rectangle vertex.
382 - * @returns {Object} diffPosition
383 - */
384 -function getReversePositionForFlip({ outDistance, startPointIndex, flipX, flipY, reatAngles }) {
385 - const rotationChangePoint1 = outDistance * Math.cos((reatAngles[0] * Math.PI) / 180);
386 - const rotationChangePoint2 = outDistance * Math.cos((reatAngles[1] * Math.PI) / 180);
387 - const isForward = startPointIndex === 2 || startPointIndex === 3;
388 - const diffPosition = {
389 - top: isForward ? rotationChangePoint1 : rotationChangePoint2,
390 - left: isForward ? rotationChangePoint2 : rotationChangePoint1,
391 - };
392 -
393 - if (isReverseLeftPositionForFlip(startPointIndex, flipX, flipY)) {
394 - diffPosition.left = diffPosition.left * -1;
395 - }
396 - if (isReverseTopPositionForFlip(startPointIndex, flipX, flipY)) {
397 - diffPosition.top = diffPosition.top * -1;
398 - }
399 -
400 - return diffPosition;
401 -}
402 -
403 -/**
404 - * Calculate a point line outside the canvas.
405 - * @param {string} type - 'x' or 'y'
406 - * @param {Array} shapePointNavigation - shape edge positions
407 - * @param {Object} shapePointNavigation.lefttop - left top position
408 - * @param {Object} shapePointNavigation.righttop - right top position
409 - * @param {Object} shapePointNavigation.leftbottom - lefttop position
410 - * @param {Object} shapePointNavigation.rightbottom - rightbottom position
411 - * @param {Array} shapeNeighborPointNavigation - Array to find adjacent edges.
412 - * @returns {Object}
413 - */
414 -function calculateLinePointsOutsideCanvas(
415 - type,
416 - shapePointNavigation,
417 - shapeNeighborPointNavigation
418 -) {
419 - let minimumPoint = 0;
420 - let minimumPointIndex = 0;
421 - forEach(shapePointNavigation, (point, index) => {
422 - if (point[type] < minimumPoint) {
423 - minimumPoint = point[type];
424 - minimumPointIndex = index;
425 - }
426 - });
427 -
428 - const [endPointIndex1, endPointIndex2] = shapeNeighborPointNavigation[minimumPointIndex];
429 -
430 - return {
431 - startPointIndex: minimumPointIndex,
432 - endPointIndex1,
433 - endPointIndex2,
434 - };
435 -}
436 -
437 -/**
438 - * Calculate a point line outside the canvas.
439 - * @param {string} type - 'x' or 'y'
440 - * @param {Array} shapePointNavigation - shape edge positions
441 - * @param {object} shapePointNavigation.lefttop - left top position
442 - * @param {object} shapePointNavigation.righttop - right top position
443 - * @param {object} shapePointNavigation.leftbottom - lefttop position
444 - * @param {object} shapePointNavigation.rightbottom - rightbottom position
445 - * @param {Object} linePointsOfOneVertexIndex - Line point of one vertex
446 - * @param {Object} linePointsOfOneVertexIndex.startPoint - start point index
447 - * @param {Object} linePointsOfOneVertexIndex.endPointIndex1 - end point index
448 - * @param {Object} linePointsOfOneVertexIndex.endPointIndex2 - end point index
449 - * @returns {Object}
450 - */
451 -function calculateLineAngleOfOutsideCanvas(type, shapePointNavigation, linePointsOfOneVertexIndex) {
452 - const { startPointIndex, endPointIndex1, endPointIndex2 } = linePointsOfOneVertexIndex;
453 - const horizontalVerticalAngle = type === 'x' ? 180 : 270;
454 -
455 - return map([endPointIndex1, endPointIndex2], (pointIndex) => {
456 - const startPoint = shapePointNavigation[startPointIndex];
457 - const endPoint = shapePointNavigation[pointIndex];
458 - const diffY = startPoint.y - endPoint.y;
459 - const diffX = startPoint.x - endPoint.x;
460 -
461 - return (Math.atan2(diffY, diffX) * 180) / Math.PI - horizontalVerticalAngle;
462 - });
463 -}
464 -
465 -/* eslint-disable complexity */
466 -/**
467 - * Calculate a point line outside the canvas for horizontal.
468 - * @param {number} startPointIndex - start point index
469 - * @param {boolean} flipX - flip x statux
470 - * @param {boolean} flipY - flip y statux
471 - * @returns {boolean} flipY - flip y statux
472 - */
473 -function isReverseLeftPositionForFlip(startPointIndex, flipX, flipY) {
474 - return (
475 - (((!flipX && flipY) || (!flipX && !flipY)) && startPointIndex === 0) ||
476 - (((flipX && flipY) || (flipX && !flipY)) && startPointIndex === 1) ||
477 - (((!flipX && !flipY) || (!flipX && flipY)) && startPointIndex === 2) ||
478 - (((flipX && !flipY) || (flipX && flipY)) && startPointIndex === 3)
479 - );
480 -}
481 -/* eslint-enable complexity */
482 -
483 -/* eslint-disable complexity */
484 -/**
485 - * Calculate a point line outside the canvas for vertical.
486 - * @param {number} startPointIndex - start point index
487 - * @param {boolean} flipX - flip x statux
488 - * @param {boolean} flipY - flip y statux
489 - * @returns {boolean} flipY - flip y statux
490 - */
491 -function isReverseTopPositionForFlip(startPointIndex, flipX, flipY) {
492 - return (
493 - (((flipX && !flipY) || (!flipX && !flipY)) && startPointIndex === 0) ||
494 - (((!flipX && !flipY) || (flipX && !flipY)) && startPointIndex === 1) ||
495 - (((flipX && flipY) || (!flipX && flipY)) && startPointIndex === 2) ||
496 - (((!flipX && flipY) || (flipX && flipY)) && startPointIndex === 3)
497 - );
498 -}
499 -/* eslint-enable complexity */
500 -
501 -/**
502 - * Shape edge points
503 - * @param {fabric.Object} shapeObj - Selected shape object on canvas
504 - * @returns {Array} shapeEdgePoint - shape edge positions
505 - */
506 -function getShapeEdgePoint(shapeObj) {
507 - return [
508 - shapeObj.getPointByOrigin('left', 'top'),
509 - shapeObj.getPointByOrigin('right', 'top'),
510 - shapeObj.getPointByOrigin('left', 'bottom'),
511 - shapeObj.getPointByOrigin('right', 'bottom'),
512 - ];
513 -}
514 -
515 -/**
516 - * Rotated shape dimension
517 - * @param {fabric.Object} shapeObj - Shape object
518 - * @returns {Object} Rotated shape dimension
519 - */
520 -function getRotatedDimension(shapeObj) {
521 - const [
522 - { x: ax, y: ay },
523 - { x: bx, y: by },
524 - { x: cx, y: cy },
525 - { x: dx, y: dy },
526 - ] = getShapeEdgePoint(shapeObj);
527 -
528 - const left = Math.min(ax, bx, cx, dx);
529 - const top = Math.min(ay, by, cy, dy);
530 - const right = Math.max(ax, bx, cx, dx);
531 - const bottom = Math.max(ay, by, cy, dy);
532 -
533 - return {
534 - left,
535 - top,
536 - right,
537 - bottom,
538 - width: right - left,
539 - height: bottom - top,
540 - };
541 -}
542 -
543 -/**
544 - * Make fill image
545 - * @param {HTMLImageElement} copiedCanvasElement - html image element
546 - * @param {number} currentCanvasImageAngle - current canvas angle
547 - * @param {Array} filterOption - filter option
548 - * @returns {fabric.Image}
549 - * @private
550 - */
551 -function makeFillImage(copiedCanvasElement, currentCanvasImageAngle, filterOption) {
552 - const fillImage = new fabric.Image(copiedCanvasElement);
553 -
554 - forEach(extend({}, ...filterOption), (value, key) => {
555 - const fabricFiterClassName = capitalizeString(key);
556 - const filter = new fabric.Image.filters[fabricFiterClassName]({
557 - [FILTER_OPTION_MAP[key]]: value,
558 - });
559 - fillImage.filters.push(filter);
560 - });
561 - fillImage.applyFilters();
562 -
563 - setCustomProperty(fillImage, {
564 - originalAngle: currentCanvasImageAngle,
565 - fillImageMaxSize: Math.max(fillImage.width, fillImage.height),
566 - });
567 - resizeHelper.adjustOriginToCenter(fillImage);
568 -
569 - return fillImage;
570 -}
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Shape resize helper
4 - */
5 -const DIVISOR = {
6 - rect: 1,
7 - circle: 2,
8 - triangle: 1,
9 -};
10 -const DIMENSION_KEYS = {
11 - rect: {
12 - w: 'width',
13 - h: 'height',
14 - },
15 - circle: {
16 - w: 'rx',
17 - h: 'ry',
18 - },
19 - triangle: {
20 - w: 'width',
21 - h: 'height',
22 - },
23 -};
24 -
25 -/**
26 - * Set the start point value to the shape object
27 - * @param {fabric.Object} shape - Shape object
28 - * @ignore
29 - */
30 -function setStartPoint(shape) {
31 - const { originX, originY } = shape;
32 - const originKey = originX.substring(0, 1) + originY.substring(0, 1);
33 -
34 - shape.startPoint = shape.origins[originKey];
35 -}
36 -
37 -/**
38 - * Get the positions of ratated origin by the pointer value
39 - * @param {{x: number, y: number}} origin - Origin value
40 - * @param {{x: number, y: number}} pointer - Pointer value
41 - * @param {number} angle - Rotating angle
42 - * @returns {Object} Postions of origin
43 - * @ignore
44 - */
45 -function getPositionsOfRotatedOrigin(origin, pointer, angle) {
46 - const sx = origin.x;
47 - const sy = origin.y;
48 - const px = pointer.x;
49 - const py = pointer.y;
50 - const r = (angle * Math.PI) / 180;
51 - const rx = (px - sx) * Math.cos(r) - (py - sy) * Math.sin(r) + sx;
52 - const ry = (px - sx) * Math.sin(r) + (py - sy) * Math.cos(r) + sy;
53 -
54 - return {
55 - originX: sx > rx ? 'right' : 'left',
56 - originY: sy > ry ? 'bottom' : 'top',
57 - };
58 -}
59 -
60 -/**
61 - * Whether the shape has the center origin or not
62 - * @param {fabric.Object} shape - Shape object
63 - * @returns {boolean} State
64 - * @ignore
65 - */
66 -function hasCenterOrigin(shape) {
67 - return shape.originX === 'center' && shape.originY === 'center';
68 -}
69 -
70 -/**
71 - * Adjust the origin of shape by the start point
72 - * @param {{x: number, y: number}} pointer - Pointer value
73 - * @param {fabric.Object} shape - Shape object
74 - * @ignore
75 - */
76 -function adjustOriginByStartPoint(pointer, shape) {
77 - const centerPoint = shape.getPointByOrigin('center', 'center');
78 - const angle = -shape.angle;
79 - const originPositions = getPositionsOfRotatedOrigin(centerPoint, pointer, angle);
80 - const { originX, originY } = originPositions;
81 - const origin = shape.getPointByOrigin(originX, originY);
82 - const left = shape.left - (centerPoint.x - origin.x);
83 - const top = shape.top - (centerPoint.y - origin.y);
84 -
85 - shape.set({
86 - originX,
87 - originY,
88 - left,
89 - top,
90 - });
91 -
92 - shape.setCoords();
93 -}
94 -
95 -/**
96 - * Adjust the origin of shape by the moving pointer value
97 - * @param {{x: number, y: number}} pointer - Pointer value
98 - * @param {fabric.Object} shape - Shape object
99 - * @ignore
100 - */
101 -function adjustOriginByMovingPointer(pointer, shape) {
102 - const origin = shape.startPoint;
103 - const angle = -shape.angle;
104 - const originPositions = getPositionsOfRotatedOrigin(origin, pointer, angle);
105 - const { originX, originY } = originPositions;
106 -
107 - shape.setPositionByOrigin(origin, originX, originY);
108 - shape.setCoords();
109 -}
110 -
111 -/**
112 - * Adjust the dimension of shape on firing scaling event
113 - * @param {fabric.Object} shape - Shape object
114 - * @ignore
115 - */
116 -function adjustDimensionOnScaling(shape) {
117 - const { type, scaleX, scaleY } = shape;
118 - const dimensionKeys = DIMENSION_KEYS[type];
119 - let width = shape[dimensionKeys.w] * scaleX;
120 - let height = shape[dimensionKeys.h] * scaleY;
121 -
122 - if (shape.isRegular) {
123 - const maxScale = Math.max(scaleX, scaleY);
124 -
125 - width = shape[dimensionKeys.w] * maxScale;
126 - height = shape[dimensionKeys.h] * maxScale;
127 - }
128 -
129 - const options = {
130 - hasControls: false,
131 - hasBorders: false,
132 - scaleX: 1,
133 - scaleY: 1,
134 - };
135 -
136 - options[dimensionKeys.w] = width;
137 - options[dimensionKeys.h] = height;
138 -
139 - shape.set(options);
140 -}
141 -
142 -/**
143 - * Adjust the dimension of shape on firing mouse move event
144 - * @param {{x: number, y: number}} pointer - Pointer value
145 - * @param {fabric.Object} shape - Shape object
146 - * @ignore
147 - */
148 -function adjustDimensionOnMouseMove(pointer, shape) {
149 - const { type, strokeWidth, startPoint: origin } = shape;
150 - const divisor = DIVISOR[type];
151 - const dimensionKeys = DIMENSION_KEYS[type];
152 - const isTriangle = !!(shape.type === 'triangle');
153 - const options = {};
154 - let width = Math.abs(origin.x - pointer.x) / divisor;
155 - let height = Math.abs(origin.y - pointer.y) / divisor;
156 -
157 - if (width > strokeWidth) {
158 - width -= strokeWidth / divisor;
159 - }
160 -
161 - if (height > strokeWidth) {
162 - height -= strokeWidth / divisor;
163 - }
164 -
165 - if (shape.isRegular) {
166 - width = height = Math.max(width, height);
167 -
168 - if (isTriangle) {
169 - height = (Math.sqrt(3) / 2) * width;
170 - }
171 - }
172 -
173 - options[dimensionKeys.w] = width;
174 - options[dimensionKeys.h] = height;
175 -
176 - shape.set(options);
177 -}
178 -
179 -module.exports = {
180 - /**
181 - * Set each origin value to shape
182 - * @param {fabric.Object} shape - Shape object
183 - */
184 - setOrigins(shape) {
185 - const leftTopPoint = shape.getPointByOrigin('left', 'top');
186 - const rightTopPoint = shape.getPointByOrigin('right', 'top');
187 - const rightBottomPoint = shape.getPointByOrigin('right', 'bottom');
188 - const leftBottomPoint = shape.getPointByOrigin('left', 'bottom');
189 -
190 - shape.origins = {
191 - lt: leftTopPoint,
192 - rt: rightTopPoint,
193 - rb: rightBottomPoint,
194 - lb: leftBottomPoint,
195 - };
196 - },
197 -
198 - /**
199 - * Resize the shape
200 - * @param {fabric.Object} shape - Shape object
201 - * @param {{x: number, y: number}} pointer - Mouse pointer values on canvas
202 - * @param {boolean} isScaling - Whether the resizing action is scaling or not
203 - */
204 - resize(shape, pointer, isScaling) {
205 - if (hasCenterOrigin(shape)) {
206 - adjustOriginByStartPoint(pointer, shape);
207 - setStartPoint(shape);
208 - }
209 -
210 - if (isScaling) {
211 - adjustDimensionOnScaling(shape, pointer);
212 - } else {
213 - adjustDimensionOnMouseMove(pointer, shape);
214 - }
215 -
216 - adjustOriginByMovingPointer(pointer, shape);
217 - },
218 -
219 - /**
220 - * Adjust the origin position of shape to center
221 - * @param {fabric.Object} shape - Shape object
222 - */
223 - adjustOriginToCenter(shape) {
224 - const centerPoint = shape.getPointByOrigin('center', 'center');
225 - const { originX, originY } = shape;
226 - const origin = shape.getPointByOrigin(originX, originY);
227 - const left = shape.left + (centerPoint.x - origin.x);
228 - const top = shape.top + (centerPoint.y - origin.y);
229 -
230 - shape.set({
231 - hasControls: true,
232 - hasBorders: true,
233 - originX: 'center',
234 - originY: 'center',
235 - left,
236 - top,
237 - });
238 -
239 - shape.setCoords(); // For left, top properties
240 - },
241 -};
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Image-editor application class
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import Invoker from './invoker';
7 -import UI from './ui';
8 -import action from './action';
9 -import commandFactory from './factory/command';
10 -import Graphics from './graphics';
11 -import { sendHostName, Promise } from './util';
12 -import { eventNames as events, commandNames as commands, keyCodes, rejectMessages } from './consts';
13 -import { makeSelectionUndoData, makeSelectionUndoDatum } from './helper/selectionModifyHelper';
14 -
15 -const { isUndefined, forEach, CustomEvents } = snippet;
16 -
17 -const {
18 - MOUSE_DOWN,
19 - OBJECT_MOVED,
20 - OBJECT_SCALED,
21 - OBJECT_ACTIVATED,
22 - OBJECT_ROTATED,
23 - OBJECT_ADDED,
24 - OBJECT_MODIFIED,
25 - ADD_TEXT,
26 - ADD_OBJECT,
27 - TEXT_EDITING,
28 - TEXT_CHANGED,
29 - ICON_CREATE_RESIZE,
30 - ICON_CREATE_END,
31 - SELECTION_CLEARED,
32 - SELECTION_CREATED,
33 - ADD_OBJECT_AFTER,
34 -} = events;
35 -
36 -/**
37 - * Image filter result
38 - * @typedef {object} FilterResult
39 - * @property {string} type - filter type like 'mask', 'Grayscale' and so on
40 - * @property {string} action - action type like 'add', 'remove'
41 - */
42 -
43 -/**
44 - * Flip status
45 - * @typedef {object} FlipStatus
46 - * @property {boolean} flipX - x axis
47 - * @property {boolean} flipY - y axis
48 - * @property {Number} angle - angle
49 - */
50 -/**
51 - * Rotation status
52 - * @typedef {Number} RotateStatus
53 - * @property {Number} angle - angle
54 - */
55 -
56 -/**
57 - * Old and new Size
58 - * @typedef {object} SizeChange
59 - * @property {Number} oldWidth - old width
60 - * @property {Number} oldHeight - old height
61 - * @property {Number} newWidth - new width
62 - * @property {Number} newHeight - new height
63 - */
64 -
65 -/**
66 - * @typedef {string} ErrorMsg - {string} error message
67 - */
68 -
69 -/**
70 - * @typedef {object} ObjectProps - graphics object properties
71 - * @property {number} id - object id
72 - * @property {string} type - object type
73 - * @property {string} text - text content
74 - * @property {(string | number)} left - Left
75 - * @property {(string | number)} top - Top
76 - * @property {(string | number)} width - Width
77 - * @property {(string | number)} height - Height
78 - * @property {string} fill - Color
79 - * @property {string} stroke - Stroke
80 - * @property {(string | number)} strokeWidth - StrokeWidth
81 - * @property {string} fontFamily - Font type for text
82 - * @property {number} fontSize - Font Size
83 - * @property {string} fontStyle - Type of inclination (normal / italic)
84 - * @property {string} fontWeight - Type of thicker or thinner looking (normal / bold)
85 - * @property {string} textAlign - Type of text align (left / center / right)
86 - * @property {string} textDecoration - Type of line (underline / line-through / overline)
87 - */
88 -
89 -/**
90 - * Shape filter option
91 - * @typedef {object.<string, number>} ShapeFilterOption
92 - */
93 -
94 -/**
95 - * Shape filter option
96 - * @typedef {object} ShapeFillOption - fill option of shape
97 - * @property {string} type - fill type ('color' or 'filter')
98 - * @property {Array.<ShapeFillFilterOption>} [filter] - {@link ShapeFilterOption} List.
99 - * only applies to filter types
100 - * (ex: \[\{pixelate: 20\}, \{blur: 0.3\}\])
101 - * @property {string} [color] - Shape foreground color (ex: '#fff', 'transparent')
102 - */
103 -
104 -/**
105 - * Image editor
106 - * @class
107 - * @param {string|HTMLElement} wrapper - Wrapper's element or selector
108 - * @param {Object} [options] - Canvas max width & height of css
109 - * @param {number} [options.includeUI] - Use the provided UI
110 - * @param {Object} [options.includeUI.loadImage] - Basic editing image
111 - * @param {string} options.includeUI.loadImage.path - image path
112 - * @param {string} options.includeUI.loadImage.name - image name
113 - * @param {Object} [options.includeUI.theme] - Theme object
114 - * @param {Array} [options.includeUI.menu] - It can be selected when only specific menu is used, Default values are \['crop', 'flip', 'rotate', 'draw', 'shape', 'icon', 'text', 'mask', 'filter'\].
115 - * @param {string} [options.includeUI.initMenu] - The first menu to be selected and started.
116 - * @param {Object} [options.includeUI.uiSize] - ui size of editor
117 - * @param {string} options.includeUI.uiSize.width - width of ui
118 - * @param {string} options.includeUI.uiSize.height - height of ui
119 - * @param {string} [options.includeUI.menuBarPosition=bottom] - Menu bar position('top', 'bottom', 'left', 'right')
120 - * @param {number} options.cssMaxWidth - Canvas css-max-width
121 - * @param {number} options.cssMaxHeight - Canvas css-max-height
122 - * @param {Object} [options.selectionStyle] - selection style
123 - * @param {string} [options.selectionStyle.cornerStyle] - selection corner style
124 - * @param {number} [options.selectionStyle.cornerSize] - selection corner size
125 - * @param {string} [options.selectionStyle.cornerColor] - selection corner color
126 - * @param {string} [options.selectionStyle.cornerStrokeColor] = selection corner stroke color
127 - * @param {boolean} [options.selectionStyle.transparentCorners] - selection corner transparent
128 - * @param {number} [options.selectionStyle.lineWidth] - selection line width
129 - * @param {string} [options.selectionStyle.borderColor] - selection border color
130 - * @param {number} [options.selectionStyle.rotatingPointOffset] - selection rotating point length
131 - * @param {Boolean} [options.usageStatistics=true] - Let us know the hostname. If you don't want to send the hostname, please set to false.
132 - * @example
133 - * var ImageEditor = require('tui-image-editor');
134 - * var blackTheme = require('./js/theme/black-theme.js');
135 - * var instance = new ImageEditor(document.querySelector('#tui-image-editor'), {
136 - * includeUI: {
137 - * loadImage: {
138 - * path: 'img/sampleImage.jpg',
139 - * name: 'SampleImage'
140 - * },
141 - * theme: blackTheme, // or whiteTheme
142 - * menu: ['shape', 'filter'],
143 - * initMenu: 'filter',
144 - * uiSize: {
145 - * width: '1000px',
146 - * height: '700px'
147 - * },
148 - * menuBarPosition: 'bottom'
149 - * },
150 - * cssMaxWidth: 700,
151 - * cssMaxHeight: 500,
152 - * selectionStyle: {
153 - * cornerSize: 20,
154 - * rotatingPointOffset: 70
155 - * }
156 - * });
157 - */
158 -class ImageEditor {
159 - constructor(wrapper, options) {
160 - options = snippet.extend(
161 - {
162 - includeUI: false,
163 - usageStatistics: true,
164 - },
165 - options
166 - );
167 -
168 - this.mode = null;
169 -
170 - this.activeObjectId = null;
171 -
172 - /**
173 - * UI instance
174 - * @type {Ui}
175 - */
176 - if (options.includeUI) {
177 - const UIOption = options.includeUI;
178 - UIOption.usageStatistics = options.usageStatistics;
179 -
180 - this.ui = new UI(wrapper, UIOption, this.getActions());
181 - options = this.ui.setUiDefaultSelectionStyle(options);
182 - }
183 -
184 - /**
185 - * Invoker
186 - * @type {Invoker}
187 - * @private
188 - */
189 - this._invoker = new Invoker();
190 -
191 - /**
192 - * Graphics instance
193 - * @type {Graphics}
194 - * @private
195 - */
196 - this._graphics = new Graphics(this.ui ? this.ui.getEditorArea() : wrapper, {
197 - cssMaxWidth: options.cssMaxWidth,
198 - cssMaxHeight: options.cssMaxHeight,
199 - });
200 -
201 - /**
202 - * Event handler list
203 - * @type {Object}
204 - * @private
205 - */
206 - this._handlers = {
207 - keydown: this._onKeyDown.bind(this),
208 - mousedown: this._onMouseDown.bind(this),
209 - objectActivated: this._onObjectActivated.bind(this),
210 - objectMoved: this._onObjectMoved.bind(this),
211 - objectScaled: this._onObjectScaled.bind(this),
212 - objectRotated: this._onObjectRotated.bind(this),
213 - objectAdded: this._onObjectAdded.bind(this),
214 - objectModified: this._onObjectModified.bind(this),
215 - createdPath: this._onCreatedPath,
216 - addText: this._onAddText.bind(this),
217 - addObject: this._onAddObject.bind(this),
218 - textEditing: this._onTextEditing.bind(this),
219 - textChanged: this._onTextChanged.bind(this),
220 - iconCreateResize: this._onIconCreateResize.bind(this),
221 - iconCreateEnd: this._onIconCreateEnd.bind(this),
222 - selectionCleared: this._selectionCleared.bind(this),
223 - selectionCreated: this._selectionCreated.bind(this),
224 - };
225 -
226 - this._attachInvokerEvents();
227 - this._attachGraphicsEvents();
228 - this._attachDomEvents();
229 - this._setSelectionStyle(options.selectionStyle, {
230 - applyCropSelectionStyle: options.applyCropSelectionStyle,
231 - applyGroupSelectionStyle: options.applyGroupSelectionStyle,
232 - });
233 -
234 - if (options.usageStatistics) {
235 - sendHostName();
236 - }
237 -
238 - if (this.ui) {
239 - this.ui.initCanvas();
240 - this.setReAction();
241 - }
242 - fabric.enableGLFiltering = false;
243 - }
244 -
245 - /**
246 - * Set selection style by init option
247 - * @param {Object} selectionStyle - Selection styles
248 - * @param {Object} applyTargets - Selection apply targets
249 - * @param {boolean} applyCropSelectionStyle - whether apply with crop selection style or not
250 - * @param {boolean} applyGroupSelectionStyle - whether apply with group selection style or not
251 - * @private
252 - */
253 - _setSelectionStyle(selectionStyle, { applyCropSelectionStyle, applyGroupSelectionStyle }) {
254 - if (selectionStyle) {
255 - this._graphics.setSelectionStyle(selectionStyle);
256 - }
257 -
258 - if (applyCropSelectionStyle) {
259 - this._graphics.setCropSelectionStyle(selectionStyle);
260 - }
261 -
262 - if (applyGroupSelectionStyle) {
263 - this.on('selectionCreated', (eventTarget) => {
264 - if (eventTarget.type === 'activeSelection') {
265 - eventTarget.set(selectionStyle);
266 - }
267 - });
268 - }
269 - }
270 -
271 - /**
272 - * Attach invoker events
273 - * @private
274 - */
275 - _attachInvokerEvents() {
276 - const { UNDO_STACK_CHANGED, REDO_STACK_CHANGED } = events;
277 -
278 - /**
279 - * Undo stack changed event
280 - * @event ImageEditor#undoStackChanged
281 - * @param {Number} length - undo stack length
282 - * @example
283 - * imageEditor.on('undoStackChanged', function(length) {
284 - * console.log(length);
285 - * });
286 - */
287 - this._invoker.on(UNDO_STACK_CHANGED, this.fire.bind(this, UNDO_STACK_CHANGED));
288 - /**
289 - * Redo stack changed event
290 - * @event ImageEditor#redoStackChanged
291 - * @param {Number} length - redo stack length
292 - * @example
293 - * imageEditor.on('redoStackChanged', function(length) {
294 - * console.log(length);
295 - * });
296 - */
297 - this._invoker.on(REDO_STACK_CHANGED, this.fire.bind(this, REDO_STACK_CHANGED));
298 - }
299 -
300 - /**
301 - * Attach canvas events
302 - * @private
303 - */
304 - _attachGraphicsEvents() {
305 - this._graphics.on({
306 - [MOUSE_DOWN]: this._handlers.mousedown,
307 - [OBJECT_MOVED]: this._handlers.objectMoved,
308 - [OBJECT_SCALED]: this._handlers.objectScaled,
309 - [OBJECT_ROTATED]: this._handlers.objectRotated,
310 - [OBJECT_ACTIVATED]: this._handlers.objectActivated,
311 - [OBJECT_ADDED]: this._handlers.objectAdded,
312 - [OBJECT_MODIFIED]: this._handlers.objectModified,
313 - [ADD_TEXT]: this._handlers.addText,
314 - [ADD_OBJECT]: this._handlers.addObject,
315 - [TEXT_EDITING]: this._handlers.textEditing,
316 - [TEXT_CHANGED]: this._handlers.textChanged,
317 - [ICON_CREATE_RESIZE]: this._handlers.iconCreateResize,
318 - [ICON_CREATE_END]: this._handlers.iconCreateEnd,
319 - [SELECTION_CLEARED]: this._handlers.selectionCleared,
320 - [SELECTION_CREATED]: this._handlers.selectionCreated,
321 - });
322 - }
323 -
324 - /**
325 - * Attach dom events
326 - * @private
327 - */
328 - _attachDomEvents() {
329 - // ImageEditor supports IE 9 higher
330 - document.addEventListener('keydown', this._handlers.keydown);
331 - }
332 -
333 - /**
334 - * Detach dom events
335 - * @private
336 - */
337 - _detachDomEvents() {
338 - // ImageEditor supports IE 9 higher
339 - document.removeEventListener('keydown', this._handlers.keydown);
340 - }
341 -
342 - /**
343 - * Keydown event handler
344 - * @param {KeyboardEvent} e - Event object
345 - * @private
346 - */
347 - /* eslint-disable complexity */
348 - _onKeyDown(e) {
349 - const { ctrlKey, keyCode, metaKey } = e;
350 - const isModifierKey = ctrlKey || metaKey;
351 -
352 - if (isModifierKey) {
353 - if (keyCode === keyCodes.C) {
354 - this._graphics.resetTargetObjectForCopyPaste();
355 - } else if (keyCode === keyCodes.V) {
356 - this._graphics.pasteObject();
357 - this.clearRedoStack();
358 - } else if (keyCode === keyCodes.Z) {
359 - // There is no error message on shortcut when it's empty
360 - this.undo()['catch'](() => {});
361 - } else if (keyCode === keyCodes.Y) {
362 - // There is no error message on shortcut when it's empty
363 - this.redo()['catch'](() => {});
364 - }
365 - }
366 -
367 - const isDeleteKey = keyCode === keyCodes.BACKSPACE || keyCode === keyCodes.DEL;
368 - const isRemoveReady = this._graphics.isReadyRemoveObject();
369 -
370 - if (isRemoveReady && isDeleteKey) {
371 - e.preventDefault();
372 - this.removeActiveObject();
373 - }
374 - }
375 -
376 - /**
377 - * Remove Active Object
378 - */
379 - removeActiveObject() {
380 - const activeObjectId = this._graphics.getActiveObjectIdForRemove();
381 -
382 - this.removeObject(activeObjectId);
383 - }
384 -
385 - /**
386 - * mouse down event handler
387 - * @param {Event} event - mouse down event
388 - * @param {Object} originPointer - origin pointer
389 - * @param {Number} originPointer.x x position
390 - * @param {Number} originPointer.y y position
391 - * @private
392 - */
393 - _onMouseDown(event, originPointer) {
394 - /**
395 - * The mouse down event with position x, y on canvas
396 - * @event ImageEditor#mousedown
397 - * @param {Object} event - browser mouse event object
398 - * @param {Object} originPointer origin pointer
399 - * @param {Number} originPointer.x x position
400 - * @param {Number} originPointer.y y position
401 - * @example
402 - * imageEditor.on('mousedown', function(event, originPointer) {
403 - * console.log(event);
404 - * console.log(originPointer);
405 - * if (imageEditor.hasFilter('colorFilter')) {
406 - * imageEditor.applyFilter('colorFilter', {
407 - * x: parseInt(originPointer.x, 10),
408 - * y: parseInt(originPointer.y, 10)
409 - * });
410 - * }
411 - * });
412 - */
413 -
414 - this.fire(events.MOUSE_DOWN, event, originPointer);
415 - }
416 -
417 - /**
418 - * Add a 'addObject' command
419 - * @param {Object} obj - Fabric object
420 - * @private
421 - */
422 - _pushAddObjectCommand(obj) {
423 - const command = commandFactory.create(commands.ADD_OBJECT, this._graphics, obj);
424 - this._invoker.pushUndoStack(command);
425 - }
426 -
427 - /**
428 - * Add a 'changeSelection' command
429 - * @param {fabric.Object} obj - selection object
430 - * @private
431 - */
432 - _pushModifyObjectCommand(obj) {
433 - const { type } = obj;
434 - const props = makeSelectionUndoData(obj, (item) =>
435 - makeSelectionUndoDatum(this._graphics.getObjectId(item), item, type === 'activeSelection')
436 - );
437 - const command = commandFactory.create(commands.CHANGE_SELECTION, this._graphics, props);
438 - command.execute(this._graphics, props);
439 -
440 - this._invoker.pushUndoStack(command);
441 - }
442 -
443 - /**
444 - * 'objectActivated' event handler
445 - * @param {ObjectProps} props - object properties
446 - * @private
447 - */
448 - _onObjectActivated(props) {
449 - /**
450 - * The event when object is selected(aka activated).
451 - * @event ImageEditor#objectActivated
452 - * @param {ObjectProps} objectProps - object properties
453 - * @example
454 - * imageEditor.on('objectActivated', function(props) {
455 - * console.log(props);
456 - * console.log(props.type);
457 - * console.log(props.id);
458 - * });
459 - */
460 - this.fire(events.OBJECT_ACTIVATED, props);
461 - }
462 -
463 - /**
464 - * 'objectMoved' event handler
465 - * @param {ObjectProps} props - object properties
466 - * @private
467 - */
468 - _onObjectMoved(props) {
469 - /**
470 - * The event when object is moved
471 - * @event ImageEditor#objectMoved
472 - * @param {ObjectProps} props - object properties
473 - * @example
474 - * imageEditor.on('objectMoved', function(props) {
475 - * console.log(props);
476 - * console.log(props.type);
477 - * });
478 - */
479 - this.fire(events.OBJECT_MOVED, props);
480 - }
481 -
482 - /**
483 - * 'objectScaled' event handler
484 - * @param {ObjectProps} props - object properties
485 - * @private
486 - */
487 - _onObjectScaled(props) {
488 - /**
489 - * The event when scale factor is changed
490 - * @event ImageEditor#objectScaled
491 - * @param {ObjectProps} props - object properties
492 - * @example
493 - * imageEditor.on('objectScaled', function(props) {
494 - * console.log(props);
495 - * console.log(props.type);
496 - * });
497 - */
498 - this.fire(events.OBJECT_SCALED, props);
499 - }
500 -
501 - /**
502 - * 'objectRotated' event handler
503 - * @param {ObjectProps} props - object properties
504 - * @private
505 - */
506 - _onObjectRotated(props) {
507 - /**
508 - * The event when object angle is changed
509 - * @event ImageEditor#objectRotated
510 - * @param {ObjectProps} props - object properties
511 - * @example
512 - * imageEditor.on('objectRotated', function(props) {
513 - * console.log(props);
514 - * console.log(props.type);
515 - * });
516 - */
517 - this.fire(events.OBJECT_ROTATED, props);
518 - }
519 -
520 - /**
521 - * Get current drawing mode
522 - * @returns {string}
523 - * @example
524 - * // Image editor drawing mode
525 - * //
526 - * // NORMAL: 'NORMAL'
527 - * // CROPPER: 'CROPPER'
528 - * // FREE_DRAWING: 'FREE_DRAWING'
529 - * // LINE_DRAWING: 'LINE_DRAWING'
530 - * // TEXT: 'TEXT'
531 - * //
532 - * if (imageEditor.getDrawingMode() === 'FREE_DRAWING') {
533 - * imageEditor.stopDrawingMode();
534 - * }
535 - */
536 - getDrawingMode() {
537 - return this._graphics.getDrawingMode();
538 - }
539 -
540 - /**
541 - * Clear all objects
542 - * @returns {Promise}
543 - * @example
544 - * imageEditor.clearObjects();
545 - */
546 - clearObjects() {
547 - return this.execute(commands.CLEAR_OBJECTS);
548 - }
549 -
550 - /**
551 - * Deactivate all objects
552 - * @example
553 - * imageEditor.deactivateAll();
554 - */
555 - deactivateAll() {
556 - this._graphics.deactivateAll();
557 - this._graphics.renderAll();
558 - }
559 -
560 - /**
561 - * discard selction
562 - * @example
563 - * imageEditor.discardSelection();
564 - */
565 - discardSelection() {
566 - this._graphics.discardSelection();
567 - }
568 -
569 - /**
570 - * selectable status change
571 - * @param {boolean} selectable - selctable status
572 - * @example
573 - * imageEditor.changeSelectableAll(false); // or true
574 - */
575 - changeSelectableAll(selectable) {
576 - this._graphics.changeSelectableAll(selectable);
577 - }
578 -
579 - /**
580 - * Invoke command
581 - * @param {String} commandName - Command name
582 - * @param {...*} args - Arguments for creating command
583 - * @returns {Promise}
584 - * @private
585 - */
586 - execute(commandName, ...args) {
587 - // Inject an Graphics instance as first parameter
588 - const theArgs = [this._graphics].concat(args);
589 -
590 - return this._invoker.execute(commandName, ...theArgs);
591 - }
592 -
593 - /**
594 - * Invoke command
595 - * @param {String} commandName - Command name
596 - * @param {...*} args - Arguments for creating command
597 - * @returns {Promise}
598 - * @private
599 - */
600 - executeSilent(commandName, ...args) {
601 - // Inject an Graphics instance as first parameter
602 - const theArgs = [this._graphics].concat(args);
603 -
604 - return this._invoker.executeSilent(commandName, ...theArgs);
605 - }
606 -
607 - /**
608 - * Undo
609 - * @returns {Promise}
610 - * @example
611 - * imageEditor.undo();
612 - */
613 - undo() {
614 - return this._invoker.undo();
615 - }
616 -
617 - /**
618 - * Redo
619 - * @returns {Promise}
620 - * @example
621 - * imageEditor.redo();
622 - */
623 - redo() {
624 - return this._invoker.redo();
625 - }
626 -
627 - /**
628 - * Load image from file
629 - * @param {File} imgFile - Image file
630 - * @param {string} [imageName] - imageName
631 - * @returns {Promise<SizeChange, ErrorMsg>}
632 - * @example
633 - * imageEditor.loadImageFromFile(file).then(result => {
634 - * console.log('old : ' + result.oldWidth + ', ' + result.oldHeight);
635 - * console.log('new : ' + result.newWidth + ', ' + result.newHeight);
636 - * });
637 - */
638 - loadImageFromFile(imgFile, imageName) {
639 - if (!imgFile) {
640 - return Promise.reject(rejectMessages.invalidParameters);
641 - }
642 -
643 - const imgUrl = URL.createObjectURL(imgFile);
644 - imageName = imageName || imgFile.name;
645 -
646 - return this.loadImageFromURL(imgUrl, imageName).then((value) => {
647 - URL.revokeObjectURL(imgFile);
648 -
649 - return value;
650 - });
651 - }
652 -
653 - /**
654 - * Load image from url
655 - * @param {string} url - File url
656 - * @param {string} imageName - imageName
657 - * @returns {Promise<SizeChange, ErrorMsg>}
658 - * @example
659 - * imageEditor.loadImageFromURL('http://url/testImage.png', 'lena').then(result => {
660 - * console.log('old : ' + result.oldWidth + ', ' + result.oldHeight);
661 - * console.log('new : ' + result.newWidth + ', ' + result.newHeight);
662 - * });
663 - */
664 - loadImageFromURL(url, imageName) {
665 - if (!imageName || !url) {
666 - return Promise.reject(rejectMessages.invalidParameters);
667 - }
668 -
669 - return this.execute(commands.LOAD_IMAGE, imageName, url);
670 - }
671 -
672 - /**
673 - * Add image object on canvas
674 - * @param {string} imgUrl - Image url to make object
675 - * @returns {Promise<ObjectProps, ErrorMsg>}
676 - * @example
677 - * imageEditor.addImageObject('path/fileName.jpg').then(objectProps => {
678 - * console.log(ojectProps.id);
679 - * });
680 - */
681 - addImageObject(imgUrl) {
682 - if (!imgUrl) {
683 - return Promise.reject(rejectMessages.invalidParameters);
684 - }
685 -
686 - return this.execute(commands.ADD_IMAGE_OBJECT, imgUrl);
687 - }
688 -
689 - /**
690 - * Start a drawing mode. If the current mode is not 'NORMAL', 'stopDrawingMode()' will be called first.
691 - * @param {String} mode Can be one of <I>'CROPPER', 'FREE_DRAWING', 'LINE_DRAWING', 'TEXT', 'SHAPE'</I>
692 - * @param {Object} [option] parameters of drawing mode, it's available with 'FREE_DRAWING', 'LINE_DRAWING'
693 - * @param {Number} [option.width] brush width
694 - * @param {String} [option.color] brush color
695 - * @param {Object} [option.arrowType] arrow decorate
696 - * @param {string} [option.arrowType.tail] arrow decorate for tail. 'chevron' or 'triangle'
697 - * @param {string} [option.arrowType.head] arrow decorate for head. 'chevron' or 'triangle'
698 - * @returns {boolean} true if success or false
699 - * @example
700 - * imageEditor.startDrawingMode('FREE_DRAWING', {
701 - * width: 10,
702 - * color: 'rgba(255,0,0,0.5)'
703 - * });
704 - * imageEditor.startDrawingMode('LINE_DRAWING', {
705 - * width: 10,
706 - * color: 'rgba(255,0,0,0.5)',
707 - * arrowType: {
708 - * tail: 'chevron' // triangle
709 - * }
710 - * });
711 - *
712 - */
713 - startDrawingMode(mode, option) {
714 - return this._graphics.startDrawingMode(mode, option);
715 - }
716 -
717 - /**
718 - * Stop the current drawing mode and back to the 'NORMAL' mode
719 - * @example
720 - * imageEditor.stopDrawingMode();
721 - */
722 - stopDrawingMode() {
723 - this._graphics.stopDrawingMode();
724 - }
725 -
726 - /**
727 - * Crop this image with rect
728 - * @param {Object} rect crop rect
729 - * @param {Number} rect.left left position
730 - * @param {Number} rect.top top position
731 - * @param {Number} rect.width width
732 - * @param {Number} rect.height height
733 - * @returns {Promise}
734 - * @example
735 - * imageEditor.crop(imageEditor.getCropzoneRect());
736 - */
737 - crop(rect) {
738 - const data = this._graphics.getCroppedImageData(rect);
739 - if (!data) {
740 - return Promise.reject(rejectMessages.invalidParameters);
741 - }
742 -
743 - return this.loadImageFromURL(data.url, data.imageName);
744 - }
745 -
746 - /**
747 - * Get the cropping rect
748 - * @returns {Object} {{left: number, top: number, width: number, height: number}} rect
749 - */
750 - getCropzoneRect() {
751 - return this._graphics.getCropzoneRect();
752 - }
753 -
754 - /**
755 - * Set the cropping rect
756 - * @param {number} [mode] crop rect mode [1, 1.5, 1.3333333333333333, 1.25, 1.7777777777777777]
757 - */
758 - setCropzoneRect(mode) {
759 - this._graphics.setCropzoneRect(mode);
760 - }
761 -
762 - /**
763 - * Flip
764 - * @returns {Promise}
765 - * @param {string} type - 'flipX' or 'flipY' or 'reset'
766 - * @returns {Promise<FlipStatus, ErrorMsg>}
767 - * @private
768 - */
769 - _flip(type) {
770 - return this.execute(commands.FLIP_IMAGE, type);
771 - }
772 -
773 - /**
774 - * Flip x
775 - * @returns {Promise<FlipStatus, ErrorMsg>}
776 - * @example
777 - * imageEditor.flipX().then((status => {
778 - * console.log('flipX: ', status.flipX);
779 - * console.log('flipY: ', status.flipY);
780 - * console.log('angle: ', status.angle);
781 - * }).catch(message => {
782 - * console.log('error: ', message);
783 - * });
784 - */
785 - flipX() {
786 - return this._flip('flipX');
787 - }
788 -
789 - /**
790 - * Flip y
791 - * @returns {Promise<FlipStatus, ErrorMsg>}
792 - * @example
793 - * imageEditor.flipY().then(status => {
794 - * console.log('flipX: ', status.flipX);
795 - * console.log('flipY: ', status.flipY);
796 - * console.log('angle: ', status.angle);
797 - * }).catch(message => {
798 - * console.log('error: ', message);
799 - * });
800 - */
801 - flipY() {
802 - return this._flip('flipY');
803 - }
804 -
805 - /**
806 - * Reset flip
807 - * @returns {Promise<FlipStatus, ErrorMsg>}
808 - * @example
809 - * imageEditor.resetFlip().then(status => {
810 - * console.log('flipX: ', status.flipX);
811 - * console.log('flipY: ', status.flipY);
812 - * console.log('angle: ', status.angle);
813 - * }).catch(message => {
814 - * console.log('error: ', message);
815 - * });;
816 - */
817 - resetFlip() {
818 - return this._flip('reset');
819 - }
820 -
821 - /**
822 - * @param {string} type - 'rotate' or 'setAngle'
823 - * @param {number} angle - angle value (degree)
824 - * @param {boolean} isSilent - is silent execution or not
825 - * @returns {Promise<RotateStatus, ErrorMsg>}
826 - * @private
827 - */
828 - _rotate(type, angle, isSilent) {
829 - let result = null;
830 - if (isSilent) {
831 - result = this.executeSilent(commands.ROTATE_IMAGE, type, angle);
832 - } else {
833 - result = this.execute(commands.ROTATE_IMAGE, type, angle);
834 - }
835 -
836 - return result;
837 - }
838 -
839 - /**
840 - * Rotate image
841 - * @returns {Promise}
842 - * @param {number} angle - Additional angle to rotate image
843 - * @param {boolean} isSilent - is silent execution or not
844 - * @returns {Promise<RotateStatus, ErrorMsg>}
845 - * @example
846 - * imageEditor.rotate(10); // angle = 10
847 - * imageEditor.rotate(10); // angle = 20
848 - * imageEidtor.rotate(5); // angle = 5
849 - * imageEidtor.rotate(-95); // angle = -90
850 - * imageEditor.rotate(10).then(status => {
851 - * console.log('angle: ', status.angle);
852 - * })).catch(message => {
853 - * console.log('error: ', message);
854 - * });
855 - */
856 - rotate(angle, isSilent) {
857 - return this._rotate('rotate', angle, isSilent);
858 - }
859 -
860 - /**
861 - * Set angle
862 - * @param {number} angle - Angle of image
863 - * @param {boolean} isSilent - is silent execution or not
864 - * @returns {Promise<RotateStatus, ErrorMsg>}
865 - * @example
866 - * imageEditor.setAngle(10); // angle = 10
867 - * imageEditor.rotate(10); // angle = 20
868 - * imageEidtor.setAngle(5); // angle = 5
869 - * imageEidtor.rotate(50); // angle = 55
870 - * imageEidtor.setAngle(-40); // angle = -40
871 - * imageEditor.setAngle(10).then(status => {
872 - * console.log('angle: ', status.angle);
873 - * })).catch(message => {
874 - * console.log('error: ', message);
875 - * });
876 - */
877 - setAngle(angle, isSilent) {
878 - return this._rotate('setAngle', angle, isSilent);
879 - }
880 -
881 - /**
882 - * Set drawing brush
883 - * @param {Object} option brush option
884 - * @param {Number} option.width width
885 - * @param {String} option.color color like 'FFFFFF', 'rgba(0, 0, 0, 0.5)'
886 - * @example
887 - * imageEditor.startDrawingMode('FREE_DRAWING');
888 - * imageEditor.setBrush({
889 - * width: 12,
890 - * color: 'rgba(0, 0, 0, 0.5)'
891 - * });
892 - * imageEditor.setBrush({
893 - * width: 8,
894 - * color: 'FFFFFF'
895 - * });
896 - */
897 - setBrush(option) {
898 - this._graphics.setBrush(option);
899 - }
900 -
901 - /**
902 - * Set states of current drawing shape
903 - * @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle')
904 - * @param {Object} [options] - Shape options
905 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
906 - * Shape foreground color (ex: '#fff', 'transparent')
907 - * @param {string} [options.stoke] - Shape outline color
908 - * @param {number} [options.strokeWidth] - Shape outline width
909 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
910 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
911 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
912 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
913 - * @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
914 - * @example
915 - * imageEditor.setDrawingShape('rect', {
916 - * fill: 'red',
917 - * width: 100,
918 - * height: 200
919 - * });
920 - * @example
921 - * imageEditor.setDrawingShape('rect', {
922 - * fill: {
923 - * type: 'filter',
924 - * filter: [{blur: 0.3}, {pixelate: 20}]
925 - * },
926 - * width: 100,
927 - * height: 200
928 - * });
929 - * @example
930 - * imageEditor.setDrawingShape('circle', {
931 - * fill: 'transparent',
932 - * stroke: 'blue',
933 - * strokeWidth: 3,
934 - * rx: 10,
935 - * ry: 100
936 - * });
937 - * @example
938 - * imageEditor.setDrawingShape('triangle', { // When resizing, the shape keep the 1:1 ratio
939 - * width: 1,
940 - * height: 1,
941 - * isRegular: true
942 - * });
943 - * @example
944 - * imageEditor.setDrawingShape('circle', { // When resizing, the shape keep the 1:1 ratio
945 - * rx: 10,
946 - * ry: 10,
947 - * isRegular: true
948 - * });
949 - */
950 - setDrawingShape(type, options) {
951 - this._graphics.setDrawingShape(type, options);
952 - }
953 -
954 - setDrawingIcon(type, iconColor) {
955 - this._graphics.setIconStyle(type, iconColor);
956 - }
957 -
958 - /**
959 - * Add shape
960 - * @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle')
961 - * @param {Object} options - Shape options
962 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
963 - * Shape foreground color (ex: '#fff', 'transparent')
964 - * @param {string} [options.stroke] - Shape outline color
965 - * @param {number} [options.strokeWidth] - Shape outline width
966 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
967 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
968 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
969 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
970 - * @param {number} [options.left] - Shape x position
971 - * @param {number} [options.top] - Shape y position
972 - * @param {boolean} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
973 - * @returns {Promise<ObjectProps, ErrorMsg>}
974 - * @example
975 - * imageEditor.addShape('rect', {
976 - * fill: 'red',
977 - * stroke: 'blue',
978 - * strokeWidth: 3,
979 - * width: 100,
980 - * height: 200,
981 - * left: 10,
982 - * top: 10,
983 - * isRegular: true
984 - * });
985 - * @example
986 - * imageEditor.addShape('circle', {
987 - * fill: 'red',
988 - * stroke: 'blue',
989 - * strokeWidth: 3,
990 - * rx: 10,
991 - * ry: 100,
992 - * isRegular: false
993 - * }).then(objectProps => {
994 - * console.log(objectProps.id);
995 - * });
996 - * @example
997 - * imageEditor.addShape('rect', {
998 - * fill: {
999 - * type: 'filter',
1000 - * filter: [{blur: 0.3}, {pixelate: 20}]
1001 - * },
1002 - * stroke: 'blue',
1003 - * strokeWidth: 3,
1004 - * rx: 10,
1005 - * ry: 100,
1006 - * isRegular: false
1007 - * }).then(objectProps => {
1008 - * console.log(objectProps.id);
1009 - * });
1010 - */
1011 - addShape(type, options) {
1012 - options = options || {};
1013 -
1014 - this._setPositions(options);
1015 -
1016 - return this.execute(commands.ADD_SHAPE, type, options);
1017 - }
1018 -
1019 - /**
1020 - * Change shape
1021 - * @param {number} id - object id
1022 - * @param {Object} options - Shape options
1023 - * @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
1024 - * Shape foreground color (ex: '#fff', 'transparent')
1025 - * @param {string} [options.stroke] - Shape outline color
1026 - * @param {number} [options.strokeWidth] - Shape outline width
1027 - * @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
1028 - * @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
1029 - * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
1030 - * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
1031 - * @param {boolean} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
1032 - * @param {boolean} isSilent - is silent execution or not
1033 - * @returns {Promise}
1034 - * @example
1035 - * // call after selecting shape object on canvas
1036 - * imageEditor.changeShape(id, { // change rectagle or triangle
1037 - * fill: 'red',
1038 - * stroke: 'blue',
1039 - * strokeWidth: 3,
1040 - * width: 100,
1041 - * height: 200
1042 - * });
1043 - * @example
1044 - * // call after selecting shape object on canvas
1045 - * imageEditor.changeShape(id, { // change circle
1046 - * fill: 'red',
1047 - * stroke: 'blue',
1048 - * strokeWidth: 3,
1049 - * rx: 10,
1050 - * ry: 100
1051 - * });
1052 - */
1053 - changeShape(id, options, isSilent) {
1054 - const executeMethodName = isSilent ? 'executeSilent' : 'execute';
1055 -
1056 - return this[executeMethodName](commands.CHANGE_SHAPE, id, options);
1057 - }
1058 -
1059 - /**
1060 - * Add text on image
1061 - * @param {string} text - Initial input text
1062 - * @param {Object} [options] Options for generating text
1063 - * @param {Object} [options.styles] Initial styles
1064 - * @param {string} [options.styles.fill] Color
1065 - * @param {string} [options.styles.fontFamily] Font type for text
1066 - * @param {number} [options.styles.fontSize] Size
1067 - * @param {string} [options.styles.fontStyle] Type of inclination (normal / italic)
1068 - * @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold)
1069 - * @param {string} [options.styles.textAlign] Type of text align (left / center / right)
1070 - * @param {string} [options.styles.textDecoration] Type of line (underline / line-through / overline)
1071 - * @param {{x: number, y: number}} [options.position] - Initial position
1072 - * @param {boolean} [options.autofocus] - text autofocus, default is true
1073 - * @returns {Promise}
1074 - * @example
1075 - * imageEditor.addText('init text');
1076 - * @example
1077 - * imageEditor.addText('init text', {
1078 - * styles: {
1079 - * fill: '#000',
1080 - * fontSize: 20,
1081 - * fontWeight: 'bold'
1082 - * },
1083 - * position: {
1084 - * x: 10,
1085 - * y: 10
1086 - * }
1087 - * }).then(objectProps => {
1088 - * console.log(objectProps.id);
1089 - * });
1090 - */
1091 - addText(text, options) {
1092 - text = text || '';
1093 - options = options || {};
1094 -
1095 - return this.execute(commands.ADD_TEXT, text, options);
1096 - }
1097 -
1098 - /**
1099 - * Change contents of selected text object on image
1100 - * @param {number} id - object id
1101 - * @param {string} text - Changing text
1102 - * @returns {Promise<ObjectProps, ErrorMsg>}
1103 - * @example
1104 - * imageEditor.changeText(id, 'change text');
1105 - */
1106 - changeText(id, text) {
1107 - text = text || '';
1108 -
1109 - return this.execute(commands.CHANGE_TEXT, id, text);
1110 - }
1111 -
1112 - /**
1113 - * Set style
1114 - * @param {number} id - object id
1115 - * @param {Object} styleObj - text styles
1116 - * @param {string} [styleObj.fill] Color
1117 - * @param {string} [styleObj.fontFamily] Font type for text
1118 - * @param {number} [styleObj.fontSize] Size
1119 - * @param {string} [styleObj.fontStyle] Type of inclination (normal / italic)
1120 - * @param {string} [styleObj.fontWeight] Type of thicker or thinner looking (normal / bold)
1121 - * @param {string} [styleObj.textAlign] Type of text align (left / center / right)
1122 - * @param {string} [styleObj.textDecoration] Type of line (underline / line-through / overline)
1123 - * @param {boolean} isSilent - is silent execution or not
1124 - * @returns {Promise}
1125 - * @example
1126 - * imageEditor.changeTextStyle(id, {
1127 - * fontStyle: 'italic'
1128 - * });
1129 - */
1130 - changeTextStyle(id, styleObj, isSilent) {
1131 - const executeMethodName = isSilent ? 'executeSilent' : 'execute';
1132 -
1133 - return this[executeMethodName](commands.CHANGE_TEXT_STYLE, id, styleObj);
1134 - }
1135 -
1136 - /**
1137 - * change text mode
1138 - * @param {string} type - change type
1139 - * @private
1140 - */
1141 - _changeActivateMode(type) {
1142 - if (type !== 'ICON' && this.getDrawingMode() !== type) {
1143 - this.startDrawingMode(type);
1144 - }
1145 - }
1146 -
1147 - /**
1148 - * 'textChanged' event handler
1149 - * @param {Object} objectProps changed object properties
1150 - * @private
1151 - */
1152 - _onTextChanged(objectProps) {
1153 - this.changeText(objectProps.id, objectProps.text);
1154 - }
1155 -
1156 - /**
1157 - * 'iconCreateResize' event handler
1158 - * @param {Object} originPointer origin pointer
1159 - * @param {Number} originPointer.x x position
1160 - * @param {Number} originPointer.y y position
1161 - * @private
1162 - */
1163 - _onIconCreateResize(originPointer) {
1164 - this.fire(events.ICON_CREATE_RESIZE, originPointer);
1165 - }
1166 -
1167 - /**
1168 - * 'iconCreateEnd' event handler
1169 - * @param {Object} originPointer origin pointer
1170 - * @param {Number} originPointer.x x position
1171 - * @param {Number} originPointer.y y position
1172 - * @private
1173 - */
1174 - _onIconCreateEnd(originPointer) {
1175 - this.fire(events.ICON_CREATE_END, originPointer);
1176 - }
1177 -
1178 - /**
1179 - * 'textEditing' event handler
1180 - * @private
1181 - */
1182 - _onTextEditing() {
1183 - /**
1184 - * The event which starts to edit text object
1185 - * @event ImageEditor#textEditing
1186 - * @example
1187 - * imageEditor.on('textEditing', function() {
1188 - * console.log('text editing');
1189 - * });
1190 - */
1191 - this.fire(events.TEXT_EDITING);
1192 - }
1193 -
1194 - /**
1195 - * Mousedown event handler in case of 'TEXT' drawing mode
1196 - * @param {fabric.Event} event - Current mousedown event object
1197 - * @private
1198 - */
1199 - _onAddText(event) {
1200 - /**
1201 - * The event when 'TEXT' drawing mode is enabled and click non-object area.
1202 - * @event ImageEditor#addText
1203 - * @param {Object} pos
1204 - * @param {Object} pos.originPosition - Current position on origin canvas
1205 - * @param {Number} pos.originPosition.x - x
1206 - * @param {Number} pos.originPosition.y - y
1207 - * @param {Object} pos.clientPosition - Current position on client area
1208 - * @param {Number} pos.clientPosition.x - x
1209 - * @param {Number} pos.clientPosition.y - y
1210 - * @example
1211 - * imageEditor.on('addText', function(pos) {
1212 - * console.log('text position on canvas: ' + pos.originPosition);
1213 - * console.log('text position on brwoser: ' + pos.clientPosition);
1214 - * });
1215 - */
1216 - this.fire(events.ADD_TEXT, {
1217 - originPosition: event.originPosition,
1218 - clientPosition: event.clientPosition,
1219 - });
1220 - }
1221 -
1222 - /**
1223 - * 'addObject' event handler
1224 - * @param {Object} objectProps added object properties
1225 - * @private
1226 - */
1227 - _onAddObject(objectProps) {
1228 - const obj = this._graphics.getObject(objectProps.id);
1229 - this._pushAddObjectCommand(obj);
1230 - }
1231 -
1232 - /**
1233 - * 'objectAdded' event handler
1234 - * @param {Object} objectProps added object properties
1235 - * @private
1236 - */
1237 - _onObjectAdded(objectProps) {
1238 - /**
1239 - * The event when object added
1240 - * @event ImageEditor#objectAdded
1241 - * @param {ObjectProps} props - object properties
1242 - * @example
1243 - * imageEditor.on('objectAdded', function(props) {
1244 - * console.log(props);
1245 - * });
1246 - */
1247 - this.fire(OBJECT_ADDED, objectProps);
1248 -
1249 - /**
1250 - * The event when object added (deprecated)
1251 - * @event ImageEditor#addObjectAfter
1252 - * @param {ObjectProps} props - object properties
1253 - * @deprecated
1254 - */
1255 - this.fire(ADD_OBJECT_AFTER, objectProps);
1256 - }
1257 -
1258 - /**
1259 - * 'objectModified' event handler
1260 - * @param {fabric.Object} obj - selection object
1261 - * @private
1262 - */
1263 - _onObjectModified(obj) {
1264 - this._pushModifyObjectCommand(obj);
1265 - }
1266 -
1267 - /**
1268 - * 'selectionCleared' event handler
1269 - * @private
1270 - */
1271 - _selectionCleared() {
1272 - this.fire(SELECTION_CLEARED);
1273 - }
1274 -
1275 - /**
1276 - * 'selectionCreated' event handler
1277 - * @param {Object} eventTarget - Fabric object
1278 - * @private
1279 - */
1280 - _selectionCreated(eventTarget) {
1281 - this.fire(SELECTION_CREATED, eventTarget);
1282 - }
1283 -
1284 - /**
1285 - * Register custom icons
1286 - * @param {{iconType: string, pathValue: string}} infos - Infos to register icons
1287 - * @example
1288 - * imageEditor.registerIcons({
1289 - * customIcon: 'M 0 0 L 20 20 L 10 10 Z',
1290 - * customArrow: 'M 60 0 L 120 60 H 90 L 75 45 V 180 H 45 V 45 L 30 60 H 0 Z'
1291 - * });
1292 - */
1293 - registerIcons(infos) {
1294 - this._graphics.registerPaths(infos);
1295 - }
1296 -
1297 - /**
1298 - * Change canvas cursor type
1299 - * @param {string} cursorType - cursor type
1300 - * @example
1301 - * imageEditor.changeCursor('crosshair');
1302 - */
1303 - changeCursor(cursorType) {
1304 - this._graphics.changeCursor(cursorType);
1305 - }
1306 -
1307 - /**
1308 - * Add icon on canvas
1309 - * @param {string} type - Icon type ('arrow', 'cancel', custom icon name)
1310 - * @param {Object} options - Icon options
1311 - * @param {string} [options.fill] - Icon foreground color
1312 - * @param {number} [options.left] - Icon x position
1313 - * @param {number} [options.top] - Icon y position
1314 - * @returns {Promise<ObjectProps, ErrorMsg>}
1315 - * @example
1316 - * imageEditor.addIcon('arrow'); // The position is center on canvas
1317 - * @example
1318 - * imageEditor.addIcon('arrow', {
1319 - * left: 100,
1320 - * top: 100
1321 - * }).then(objectProps => {
1322 - * console.log(objectProps.id);
1323 - * });
1324 - */
1325 - addIcon(type, options) {
1326 - options = options || {};
1327 -
1328 - this._setPositions(options);
1329 -
1330 - return this.execute(commands.ADD_ICON, type, options);
1331 - }
1332 -
1333 - /**
1334 - * Change icon color
1335 - * @param {number} id - object id
1336 - * @param {string} color - Color for icon
1337 - * @returns {Promise}
1338 - * @example
1339 - * imageEditor.changeIconColor(id, '#000000');
1340 - */
1341 - changeIconColor(id, color) {
1342 - return this.execute(commands.CHANGE_ICON_COLOR, id, color);
1343 - }
1344 -
1345 - /**
1346 - * Remove an object or group by id
1347 - * @param {number} id - object id
1348 - * @returns {Promise}
1349 - * @example
1350 - * imageEditor.removeObject(id);
1351 - */
1352 - removeObject(id) {
1353 - return this.execute(commands.REMOVE_OBJECT, id);
1354 - }
1355 -
1356 - /**
1357 - * Whether it has the filter or not
1358 - * @param {string} type - Filter type
1359 - * @returns {boolean} true if it has the filter
1360 - */
1361 - hasFilter(type) {
1362 - return this._graphics.hasFilter(type);
1363 - }
1364 -
1365 - /**
1366 - * Remove filter on canvas image
1367 - * @param {string} type - Filter type
1368 - * @returns {Promise<FilterResult, ErrorMsg>}
1369 - * @example
1370 - * imageEditor.removeFilter('Grayscale').then(obj => {
1371 - * console.log('filterType: ', obj.type);
1372 - * console.log('actType: ', obj.action);
1373 - * }).catch(message => {
1374 - * console.log('error: ', message);
1375 - * });
1376 - */
1377 - removeFilter(type) {
1378 - return this.execute(commands.REMOVE_FILTER, type);
1379 - }
1380 -
1381 - /**
1382 - * Apply filter on canvas image
1383 - * @param {string} type - Filter type
1384 - * @param {Object} options - Options to apply filter
1385 - * @param {number} options.maskObjId - masking image object id
1386 - * @param {boolean} isSilent - is silent execution or not
1387 - * @returns {Promise<FilterResult, ErrorMsg>}
1388 - * @example
1389 - * imageEditor.applyFilter('Grayscale');
1390 - * @example
1391 - * imageEditor.applyFilter('mask', {maskObjId: id}).then(obj => {
1392 - * console.log('filterType: ', obj.type);
1393 - * console.log('actType: ', obj.action);
1394 - * }).catch(message => {
1395 - * console.log('error: ', message);
1396 - * });;
1397 - */
1398 - applyFilter(type, options, isSilent) {
1399 - const executeMethodName = isSilent ? 'executeSilent' : 'execute';
1400 -
1401 - return this[executeMethodName](commands.APPLY_FILTER, type, options);
1402 - }
1403 -
1404 - /**
1405 - * Get data url
1406 - * @param {Object} options - options for toDataURL
1407 - * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
1408 - * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
1409 - * @param {Number} [options.multiplier=1] Multiplier to scale by
1410 - * @param {Number} [options.left] Cropping left offset. Introduced in fabric v1.2.14
1411 - * @param {Number} [options.top] Cropping top offset. Introduced in fabric v1.2.14
1412 - * @param {Number} [options.width] Cropping width. Introduced in fabric v1.2.14
1413 - * @param {Number} [options.height] Cropping height. Introduced in fabric v1.2.14
1414 - * @returns {string} A DOMString containing the requested data URI
1415 - * @example
1416 - * imgEl.src = imageEditor.toDataURL();
1417 - *
1418 - * imageEditor.loadImageFromURL(imageEditor.toDataURL(), 'FilterImage').then(() => {
1419 - * imageEditor.addImageObject(imgUrl);
1420 - * });
1421 - */
1422 - toDataURL(options) {
1423 - return this._graphics.toDataURL(options);
1424 - }
1425 -
1426 - /**
1427 - * Get image name
1428 - * @returns {string} image name
1429 - * @example
1430 - * console.log(imageEditor.getImageName());
1431 - */
1432 - getImageName() {
1433 - return this._graphics.getImageName();
1434 - }
1435 -
1436 - /**
1437 - * Clear undoStack
1438 - * @example
1439 - * imageEditor.clearUndoStack();
1440 - */
1441 - clearUndoStack() {
1442 - this._invoker.clearUndoStack();
1443 - }
1444 -
1445 - /**
1446 - * Clear redoStack
1447 - * @example
1448 - * imageEditor.clearRedoStack();
1449 - */
1450 - clearRedoStack() {
1451 - this._invoker.clearRedoStack();
1452 - }
1453 -
1454 - /**
1455 - * Whehter the undo stack is empty or not
1456 - * @returns {boolean}
1457 - * imageEditor.isEmptyUndoStack();
1458 - */
1459 - isEmptyUndoStack() {
1460 - return this._invoker.isEmptyUndoStack();
1461 - }
1462 -
1463 - /**
1464 - * Whehter the redo stack is empty or not
1465 - * @returns {boolean}
1466 - * imageEditor.isEmptyRedoStack();
1467 - */
1468 - isEmptyRedoStack() {
1469 - return this._invoker.isEmptyRedoStack();
1470 - }
1471 -
1472 - /**
1473 - * Resize canvas dimension
1474 - * @param {{width: number, height: number}} dimension - Max width & height
1475 - * @returns {Promise}
1476 - */
1477 - resizeCanvasDimension(dimension) {
1478 - if (!dimension) {
1479 - return Promise.reject(rejectMessages.invalidParameters);
1480 - }
1481 -
1482 - return this.execute(commands.RESIZE_CANVAS_DIMENSION, dimension);
1483 - }
1484 -
1485 - /**
1486 - * Destroy
1487 - */
1488 - destroy() {
1489 - this.stopDrawingMode();
1490 - this._detachDomEvents();
1491 - this._graphics.destroy();
1492 - this._graphics = null;
1493 -
1494 - if (this.ui) {
1495 - this.ui.destroy();
1496 - }
1497 -
1498 - forEach(
1499 - this,
1500 - (value, key) => {
1501 - this[key] = null;
1502 - },
1503 - this
1504 - );
1505 - }
1506 -
1507 - /**
1508 - * Set position
1509 - * @param {Object} options - Position options (left or top)
1510 - * @private
1511 - */
1512 - _setPositions(options) {
1513 - const centerPosition = this._graphics.getCenter();
1514 -
1515 - if (isUndefined(options.left)) {
1516 - options.left = centerPosition.left;
1517 - }
1518 -
1519 - if (isUndefined(options.top)) {
1520 - options.top = centerPosition.top;
1521 - }
1522 - }
1523 -
1524 - /**
1525 - * Set properties of active object
1526 - * @param {number} id - object id
1527 - * @param {Object} keyValue - key & value
1528 - * @returns {Promise}
1529 - * @example
1530 - * imageEditor.setObjectProperties(id, {
1531 - * left:100,
1532 - * top:100,
1533 - * width: 200,
1534 - * height: 200,
1535 - * opacity: 0.5
1536 - * });
1537 - */
1538 - setObjectProperties(id, keyValue) {
1539 - return this.execute(commands.SET_OBJECT_PROPERTIES, id, keyValue);
1540 - }
1541 -
1542 - /**
1543 - * Set properties of active object, Do not leave an invoke history.
1544 - * @param {number} id - object id
1545 - * @param {Object} keyValue - key & value
1546 - * @example
1547 - * imageEditor.setObjectPropertiesQuietly(id, {
1548 - * left:100,
1549 - * top:100,
1550 - * width: 200,
1551 - * height: 200,
1552 - * opacity: 0.5
1553 - * });
1554 - */
1555 - setObjectPropertiesQuietly(id, keyValue) {
1556 - this._graphics.setObjectProperties(id, keyValue);
1557 - }
1558 -
1559 - /**
1560 - * Get properties of active object corresponding key
1561 - * @param {number} id - object id
1562 - * @param {Array<string>|ObjectProps|string} keys - property's key
1563 - * @returns {ObjectProps} properties if id is valid or null
1564 - * @example
1565 - * var props = imageEditor.getObjectProperties(id, 'left');
1566 - * console.log(props);
1567 - * @example
1568 - * var props = imageEditor.getObjectProperties(id, ['left', 'top', 'width', 'height']);
1569 - * console.log(props);
1570 - * @example
1571 - * var props = imageEditor.getObjectProperties(id, {
1572 - * left: null,
1573 - * top: null,
1574 - * width: null,
1575 - * height: null,
1576 - * opacity: null
1577 - * });
1578 - * console.log(props);
1579 - */
1580 - getObjectProperties(id, keys) {
1581 - const object = this._graphics.getObject(id);
1582 - if (!object) {
1583 - return null;
1584 - }
1585 -
1586 - return this._graphics.getObjectProperties(id, keys);
1587 - }
1588 -
1589 - /**
1590 - * Get the canvas size
1591 - * @returns {Object} {{width: number, height: number}} canvas size
1592 - * @example
1593 - * var canvasSize = imageEditor.getCanvasSize();
1594 - * console.log(canvasSize.width);
1595 - * console.height(canvasSize.height);
1596 - */
1597 - getCanvasSize() {
1598 - return this._graphics.getCanvasSize();
1599 - }
1600 -
1601 - /**
1602 - * Get object position by originX, originY
1603 - * @param {number} id - object id
1604 - * @param {string} originX - can be 'left', 'center', 'right'
1605 - * @param {string} originY - can be 'top', 'center', 'bottom'
1606 - * @returns {Object} {{x:number, y: number}} position by origin if id is valid, or null
1607 - * @example
1608 - * var position = imageEditor.getObjectPosition(id, 'left', 'top');
1609 - * console.log(position);
1610 - */
1611 - getObjectPosition(id, originX, originY) {
1612 - return this._graphics.getObjectPosition(id, originX, originY);
1613 - }
1614 -
1615 - /**
1616 - * Set object position by originX, originY
1617 - * @param {number} id - object id
1618 - * @param {Object} posInfo - position object
1619 - * @param {number} posInfo.x - x position
1620 - * @param {number} posInfo.y - y position
1621 - * @param {string} posInfo.originX - can be 'left', 'center', 'right'
1622 - * @param {string} posInfo.originY - can be 'top', 'center', 'bottom'
1623 - * @returns {Promise}
1624 - * @example
1625 - * // align the object to 'left', 'top'
1626 - * imageEditor.setObjectPosition(id, {
1627 - * x: 0,
1628 - * y: 0,
1629 - * originX: 'left',
1630 - * originY: 'top'
1631 - * });
1632 - * @example
1633 - * // align the object to 'right', 'top'
1634 - * var canvasSize = imageEditor.getCanvasSize();
1635 - * imageEditor.setObjectPosition(id, {
1636 - * x: canvasSize.width,
1637 - * y: 0,
1638 - * originX: 'right',
1639 - * originY: 'top'
1640 - * });
1641 - * @example
1642 - * // align the object to 'left', 'bottom'
1643 - * var canvasSize = imageEditor.getCanvasSize();
1644 - * imageEditor.setObjectPosition(id, {
1645 - * x: 0,
1646 - * y: canvasSize.height,
1647 - * originX: 'left',
1648 - * originY: 'bottom'
1649 - * });
1650 - * @example
1651 - * // align the object to 'right', 'bottom'
1652 - * var canvasSize = imageEditor.getCanvasSize();
1653 - * imageEditor.setObjectPosition(id, {
1654 - * x: canvasSize.width,
1655 - * y: canvasSize.height,
1656 - * originX: 'right',
1657 - * originY: 'bottom'
1658 - * });
1659 - */
1660 - setObjectPosition(id, posInfo) {
1661 - return this.execute(commands.SET_OBJECT_POSITION, id, posInfo);
1662 - }
1663 -}
1664 -
1665 -action.mixin(ImageEditor);
1666 -CustomEvents.mixin(ImageEditor);
1667 -
1668 -export default ImageEditor;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Command interface
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import errorMessage from '../factory/errorMessage';
7 -
8 -const createMessage = errorMessage.create;
9 -const errorTypes = errorMessage.types;
10 -
11 -/**
12 - * Command class
13 - * @class
14 - * @param {{name:function, execute: function, undo: function,
15 - * executeCallback: function, undoCallback: function}} actions - Command actions
16 - * @param {Array} args - passing arguments on execute, undo
17 - * @ignore
18 - */
19 -class Command {
20 - constructor(actions, args) {
21 - /**
22 - * command name
23 - * @type {string}
24 - */
25 - this.name = actions.name;
26 -
27 - /**
28 - * arguments
29 - * @type {Array}
30 - */
31 - this.args = args;
32 -
33 - /**
34 - * Execute function
35 - * @type {function}
36 - */
37 - this.execute = actions.execute;
38 -
39 - /**
40 - * Undo function
41 - * @type {function}
42 - */
43 - this.undo = actions.undo;
44 -
45 - /**
46 - * executeCallback
47 - * @type {function}
48 - */
49 - this.executeCallback = actions.executeCallback || null;
50 -
51 - /**
52 - * undoCallback
53 - * @type {function}
54 - */
55 - this.undoCallback = actions.undoCallback || null;
56 -
57 - /**
58 - * data for undo
59 - * @type {Object}
60 - */
61 - this.undoData = {};
62 - }
63 -
64 - /**
65 - * Execute action
66 - * @param {Object.<string, Component>} compMap - Components injection
67 - * @abstract
68 - */
69 - execute() {
70 - throw new Error(createMessage(errorTypes.UN_IMPLEMENTATION, 'execute'));
71 - }
72 -
73 - /**
74 - * Undo action
75 - * @param {Object.<string, Component>} compMap - Components injection
76 - * @abstract
77 - */
78 - undo() {
79 - throw new Error(createMessage(errorTypes.UN_IMPLEMENTATION, 'undo'));
80 - }
81 -
82 - /**
83 - * command for redo if undoData exists
84 - * @returns {boolean} isRedo
85 - */
86 - get isRedo() {
87 - return Object.keys(this.undoData).length;
88 - }
89 -
90 - /**
91 - * Set undoData action
92 - * @param {Object} undoData - maked undo data
93 - * @param {Object} cachedUndoDataForSilent - cached undo data
94 - * @param {boolean} isSilent - is silent execution or not
95 - * @returns {Object} cachedUndoDataForSilent
96 - */
97 - setUndoData(undoData, cachedUndoDataForSilent, isSilent) {
98 - if (cachedUndoDataForSilent) {
99 - undoData = cachedUndoDataForSilent;
100 - }
101 -
102 - if (!isSilent) {
103 - snippet.extend(this.undoData, undoData);
104 - cachedUndoDataForSilent = null;
105 - } else if (!cachedUndoDataForSilent) {
106 - cachedUndoDataForSilent = undoData;
107 - }
108 -
109 - return cachedUndoDataForSilent;
110 - }
111 -
112 - /**
113 - * Attach execute callabck
114 - * @param {function} callback - Callback after execution
115 - * @returns {Command} this
116 - */
117 - setExecuteCallback(callback) {
118 - this.executeCallback = callback;
119 -
120 - return this;
121 - }
122 -
123 - /**
124 - * Attach undo callback
125 - * @param {function} callback - Callback after undo
126 - * @returns {Command} this
127 - */
128 - setUndoCallback(callback) {
129 - this.undoCallback = callback;
130 -
131 - return this;
132 - }
133 -}
134 -
135 -export default Command;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Component interface
4 - */
5 -
6 -/**
7 - * Component interface
8 - * @class
9 - * @param {string} name - component name
10 - * @param {Graphics} graphics - Graphics instance
11 - * @ignore
12 - */
13 -class Component {
14 - constructor(name, graphics) {
15 - /**
16 - * Component name
17 - * @type {string}
18 - */
19 - this.name = name;
20 -
21 - /**
22 - * Graphics instance
23 - * @type {Graphics}
24 - */
25 - this.graphics = graphics;
26 - }
27 -
28 - /**
29 - * Fire Graphics event
30 - * @returns {Object} return value
31 - */
32 - fire(...args) {
33 - const context = this.graphics;
34 -
35 - return this.graphics.fire.apply(context, args);
36 - }
37 -
38 - /**
39 - * Save image(background) of canvas
40 - * @param {string} name - Name of image
41 - * @param {fabric.Image} oImage - Fabric image instance
42 - */
43 - setCanvasImage(name, oImage) {
44 - this.graphics.setCanvasImage(name, oImage);
45 - }
46 -
47 - /**
48 - * Returns canvas element of fabric.Canvas[[lower-canvas]]
49 - * @returns {HTMLCanvasElement}
50 - */
51 - getCanvasElement() {
52 - return this.graphics.getCanvasElement();
53 - }
54 -
55 - /**
56 - * Get fabric.Canvas instance
57 - * @returns {fabric.Canvas}
58 - */
59 - getCanvas() {
60 - return this.graphics.getCanvas();
61 - }
62 -
63 - /**
64 - * Get canvasImage (fabric.Image instance)
65 - * @returns {fabric.Image}
66 - */
67 - getCanvasImage() {
68 - return this.graphics.getCanvasImage();
69 - }
70 -
71 - /**
72 - * Get image name
73 - * @returns {string}
74 - */
75 - getImageName() {
76 - return this.graphics.getImageName();
77 - }
78 -
79 - /**
80 - * Get image editor
81 - * @returns {ImageEditor}
82 - */
83 - getEditor() {
84 - return this.graphics.getEditor();
85 - }
86 -
87 - /**
88 - * Return component name
89 - * @returns {string}
90 - */
91 - getName() {
92 - return this.name;
93 - }
94 -
95 - /**
96 - * Set image properties
97 - * @param {Object} setting - Image properties
98 - * @param {boolean} [withRendering] - If true, The changed image will be reflected in the canvas
99 - */
100 - setImageProperties(setting, withRendering) {
101 - this.graphics.setImageProperties(setting, withRendering);
102 - }
103 -
104 - /**
105 - * Set canvas dimension - css only
106 - * @param {Object} dimension - Canvas css dimension
107 - */
108 - setCanvasCssDimension(dimension) {
109 - this.graphics.setCanvasCssDimension(dimension);
110 - }
111 -
112 - /**
113 - * Set canvas dimension - css only
114 - * @param {Object} dimension - Canvas backstore dimension
115 - */
116 - setCanvasBackstoreDimension(dimension) {
117 - this.graphics.setCanvasBackstoreDimension(dimension);
118 - }
119 -
120 - /**
121 - * Adjust canvas dimension with scaling image
122 - */
123 - adjustCanvasDimension() {
124 - this.graphics.adjustCanvasDimension();
125 - }
126 -}
127 -
128 -export default Component;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview DrawingMode interface
4 - */
5 -import errorMessage from '../factory/errorMessage';
6 -
7 -const createMessage = errorMessage.create;
8 -const errorTypes = errorMessage.types;
9 -
10 -/**
11 - * DrawingMode interface
12 - * @class
13 - * @param {string} name - drawing mode name
14 - * @ignore
15 - */
16 -class DrawingMode {
17 - constructor(name) {
18 - /**
19 - * the name of drawing mode
20 - * @type {string}
21 - */
22 - this.name = name;
23 - }
24 -
25 - /**
26 - * Get this drawing mode name;
27 - * @returns {string} drawing mode name
28 - */
29 - getName() {
30 - return this.name;
31 - }
32 -
33 - /**
34 - * start this drawing mode
35 - * @param {Object} options - drawing mode options
36 - * @abstract
37 - */
38 - start() {
39 - throw new Error(createMessage(errorTypes.UN_IMPLEMENTATION, 'start'));
40 - }
41 -
42 - /**
43 - * stop this drawing mode
44 - * @abstract
45 - */
46 - stop() {
47 - throw new Error(createMessage(errorTypes.UN_IMPLEMENTATION, 'stop'));
48 - }
49 -}
50 -
51 -export default DrawingMode;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Invoker - invoke commands
4 - */
5 -import snippet from 'tui-code-snippet';
6 -import { Promise } from './util';
7 -import commandFactory from './factory/command';
8 -import { eventNames, rejectMessages } from './consts';
9 -
10 -const { isFunction, isString, CustomEvents } = snippet;
11 -
12 -/**
13 - * Invoker
14 - * @class
15 - * @ignore
16 - */
17 -class Invoker {
18 - constructor() {
19 - /**
20 - * Undo stack
21 - * @type {Array.<Command>}
22 - * @private
23 - */
24 - this._undoStack = [];
25 -
26 - /**
27 - * Redo stack
28 - * @type {Array.<Command>}
29 - * @private
30 - */
31 - this._redoStack = [];
32 -
33 - /**
34 - * Lock-flag for executing command
35 - * @type {boolean}
36 - * @private
37 - */
38 - this._isLocked = false;
39 -
40 - this._isSilent = false;
41 - }
42 -
43 - /**
44 - * Invoke command execution
45 - * @param {Command} command - Command
46 - * @returns {Promise}
47 - * @private
48 - */
49 - _invokeExecution(command) {
50 - this.lock();
51 -
52 - let { args } = command;
53 - if (!args) {
54 - args = [];
55 - }
56 -
57 - return command
58 - .execute(...args)
59 - .then((value) => {
60 - if (!this._isSilent) {
61 - this.pushUndoStack(command);
62 - }
63 - this.unlock();
64 - if (isFunction(command.executeCallback)) {
65 - command.executeCallback(value);
66 - }
67 -
68 - return value;
69 - })
70 - ['catch']((message) => {
71 - this.unlock();
72 -
73 - return Promise.reject(message);
74 - });
75 - }
76 -
77 - /**
78 - * Invoke command undo
79 - * @param {Command} command - Command
80 - * @returns {Promise}
81 - * @private
82 - */
83 - _invokeUndo(command) {
84 - this.lock();
85 -
86 - let { args } = command;
87 - if (!args) {
88 - args = [];
89 - }
90 -
91 - return command
92 - .undo(...args)
93 - .then((value) => {
94 - this.pushRedoStack(command);
95 - this.unlock();
96 - if (isFunction(command.undoCallback)) {
97 - command.undoCallback(value);
98 - }
99 -
100 - return value;
101 - })
102 - ['catch']((message) => {
103 - this.unlock();
104 -
105 - return Promise.reject(message);
106 - });
107 - }
108 -
109 - /**
110 - * fire REDO_STACK_CHANGED event
111 - * @private
112 - */
113 - _fireRedoStackChanged() {
114 - this.fire(eventNames.REDO_STACK_CHANGED, this._redoStack.length);
115 - }
116 -
117 - /**
118 - * fire UNDO_STACK_CHANGED event
119 - * @private
120 - */
121 - _fireUndoStackChanged() {
122 - this.fire(eventNames.UNDO_STACK_CHANGED, this._undoStack.length);
123 - }
124 -
125 - /**
126 - * Lock this invoker
127 - */
128 - lock() {
129 - this._isLocked = true;
130 - }
131 -
132 - /**
133 - * Unlock this invoker
134 - */
135 - unlock() {
136 - this._isLocked = false;
137 - }
138 -
139 - executeSilent(...args) {
140 - this._isSilent = true;
141 -
142 - return this.execute(...args, this._isSilent).then(() => {
143 - this._isSilent = false;
144 - });
145 - }
146 -
147 - /**
148 - * Invoke command
149 - * Store the command to the undoStack
150 - * Clear the redoStack
151 - * @param {String} commandName - Command name
152 - * @param {...*} args - Arguments for creating command
153 - * @returns {Promise}
154 - */
155 - execute(...args) {
156 - if (this._isLocked) {
157 - return Promise.reject(rejectMessages.isLock);
158 - }
159 -
160 - let [command] = args;
161 - if (isString(command)) {
162 - command = commandFactory.create(...args);
163 - }
164 -
165 - return this._invokeExecution(command).then((value) => {
166 - this.clearRedoStack();
167 -
168 - return value;
169 - });
170 - }
171 -
172 - /**
173 - * Undo command
174 - * @returns {Promise}
175 - */
176 - undo() {
177 - let command = this._undoStack.pop();
178 - let promise;
179 - let message = '';
180 -
181 - if (command && this._isLocked) {
182 - this.pushUndoStack(command, true);
183 - command = null;
184 - }
185 - if (command) {
186 - if (this.isEmptyUndoStack()) {
187 - this._fireUndoStackChanged();
188 - }
189 - promise = this._invokeUndo(command);
190 - } else {
191 - message = rejectMessages.undo;
192 - if (this._isLocked) {
193 - message = `${message} Because ${rejectMessages.isLock}`;
194 - }
195 - promise = Promise.reject(message);
196 - }
197 -
198 - return promise;
199 - }
200 -
201 - /**
202 - * Redo command
203 - * @returns {Promise}
204 - */
205 - redo() {
206 - let command = this._redoStack.pop();
207 - let promise;
208 - let message = '';
209 -
210 - if (command && this._isLocked) {
211 - this.pushRedoStack(command, true);
212 - command = null;
213 - }
214 - if (command) {
215 - if (this.isEmptyRedoStack()) {
216 - this._fireRedoStackChanged();
217 - }
218 - promise = this._invokeExecution(command);
219 - } else {
220 - message = rejectMessages.redo;
221 - if (this._isLocked) {
222 - message = `${message} Because ${rejectMessages.isLock}`;
223 - }
224 - promise = Promise.reject(message);
225 - }
226 -
227 - return promise;
228 - }
229 -
230 - /**
231 - * Push undo stack
232 - * @param {Command} command - command
233 - * @param {boolean} [isSilent] - Fire event or not
234 - */
235 - pushUndoStack(command, isSilent) {
236 - this._undoStack.push(command);
237 - if (!isSilent) {
238 - this._fireUndoStackChanged();
239 - }
240 - }
241 -
242 - /**
243 - * Push redo stack
244 - * @param {Command} command - command
245 - * @param {boolean} [isSilent] - Fire event or not
246 - */
247 - pushRedoStack(command, isSilent) {
248 - this._redoStack.push(command);
249 - if (!isSilent) {
250 - this._fireRedoStackChanged();
251 - }
252 - }
253 -
254 - /**
255 - * Return whether the redoStack is empty
256 - * @returns {boolean}
257 - */
258 - isEmptyRedoStack() {
259 - return this._redoStack.length === 0;
260 - }
261 -
262 - /**
263 - * Return whether the undoStack is empty
264 - * @returns {boolean}
265 - */
266 - isEmptyUndoStack() {
267 - return this._undoStack.length === 0;
268 - }
269 -
270 - /**
271 - * Clear undoStack
272 - */
273 - clearUndoStack() {
274 - if (!this.isEmptyUndoStack()) {
275 - this._undoStack = [];
276 - this._fireUndoStackChanged();
277 - }
278 - }
279 -
280 - /**
281 - * Clear redoStack
282 - */
283 - clearRedoStack() {
284 - if (!this.isEmptyRedoStack()) {
285 - this._redoStack = [];
286 - this._fireRedoStackChanged();
287 - }
288 - }
289 -}
290 -
291 -CustomEvents.mixin(Invoker);
292 -
293 -export default Invoker;
1 -// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
2 -// Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/if (!Element.prototype.matches)
3 -Element.prototype.matches =
4 - Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
5 -
6 -if (!Element.prototype.closest)
7 - Element.prototype.closest = function (s) {
8 - var el = this;
9 - if (!document.documentElement.contains(el)) return null;
10 - do {
11 - if (el.matches(s)) return el;
12 - el = el.parentElement || el.parentNode;
13 - } while (el !== null && el.nodeType === 1);
14 - return null;
15 - };
16 -
17 -/*
18 - * classList.js: Cross-browser full element.classList implementation.
19 - * 1.1.20170427
20 - *
21 - * By Eli Grey, http://eligrey.com
22 - * License: Dedicated to the public domain.
23 - * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
24 - */
25 -
26 -/*global self, document, DOMException */
27 -
28 -/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
29 -
30 -if ('document' in window.self) {
31 - // Full polyfill for browsers with no classList support
32 - // Including IE < Edge missing SVGElement.classList
33 - if (
34 - !('classList' in document.createElement('_')) ||
35 - (document.createElementNS &&
36 - !('classList' in document.createElementNS('http://www.w3.org/2000/svg', 'g')))
37 - ) {
38 - (function (view) {
39 - 'use strict';
40 -
41 - if (!('Element' in view)) return;
42 -
43 - var classListProp = 'classList',
44 - protoProp = 'prototype',
45 - elemCtrProto = view.Element[protoProp],
46 - objCtr = Object,
47 - strTrim =
48 - String[protoProp].trim ||
49 - function () {
50 - return this.replace(/^\s+|\s+$/g, '');
51 - },
52 - arrIndexOf =
53 - Array[protoProp].indexOf ||
54 - function (item) {
55 - var i = 0,
56 - len = this.length;
57 - for (; i < len; i++) {
58 - if (i in this && this[i] === item) {
59 - return i;
60 - }
61 - }
62 - return -1;
63 - },
64 - // Vendors: please allow content code to instantiate DOMExceptions
65 - DOMEx = function (type, message) {
66 - this.name = type;
67 - this.code = DOMException[type];
68 - this.message = message;
69 - },
70 - checkTokenAndGetIndex = function (classList, token) {
71 - if (token === '') {
72 - throw new DOMEx('SYNTAX_ERR', 'An invalid or illegal string was specified');
73 - }
74 - if (/\s/.test(token)) {
75 - throw new DOMEx('INVALID_CHARACTER_ERR', 'String contains an invalid character');
76 - }
77 - return arrIndexOf.call(classList, token);
78 - },
79 - ClassList = function (elem) {
80 - var trimmedClasses = strTrim.call(elem.getAttribute('class') || ''),
81 - classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
82 - i = 0,
83 - len = classes.length;
84 - for (; i < len; i++) {
85 - this.push(classes[i]);
86 - }
87 - this._updateClassName = function () {
88 - elem.setAttribute('class', this.toString());
89 - };
90 - },
91 - classListProto = (ClassList[protoProp] = []),
92 - classListGetter = function () {
93 - return new ClassList(this);
94 - };
95 - // Most DOMException implementations don't allow calling DOMException's toString()
96 - // on non-DOMExceptions. Error's toString() is sufficient here.
97 - DOMEx[protoProp] = Error[protoProp];
98 - classListProto.item = function (i) {
99 - return this[i] || null;
100 - };
101 - classListProto.contains = function (token) {
102 - token += '';
103 - return checkTokenAndGetIndex(this, token) !== -1;
104 - };
105 - classListProto.add = function () {
106 - var tokens = arguments,
107 - i = 0,
108 - l = tokens.length,
109 - token,
110 - updated = false;
111 - do {
112 - token = tokens[i] + '';
113 - if (checkTokenAndGetIndex(this, token) === -1) {
114 - this.push(token);
115 - updated = true;
116 - }
117 - } while (++i < l);
118 -
119 - if (updated) {
120 - this._updateClassName();
121 - }
122 - };
123 - classListProto.remove = function () {
124 - var tokens = arguments,
125 - i = 0,
126 - l = tokens.length,
127 - token,
128 - updated = false,
129 - index;
130 - do {
131 - token = tokens[i] + '';
132 - index = checkTokenAndGetIndex(this, token);
133 - while (index !== -1) {
134 - this.splice(index, 1);
135 - updated = true;
136 - index = checkTokenAndGetIndex(this, token);
137 - }
138 - } while (++i < l);
139 -
140 - if (updated) {
141 - this._updateClassName();
142 - }
143 - };
144 - classListProto.toggle = function (token, force) {
145 - token += '';
146 -
147 - var result = this.contains(token),
148 - method = result ? force !== true && 'remove' : force !== false && 'add';
149 - if (method) {
150 - this[method](token);
151 - }
152 -
153 - if (force === true || force === false) {
154 - return force;
155 - } else {
156 - return !result;
157 - }
158 - };
159 - classListProto.toString = function () {
160 - return this.join(' ');
161 - };
162 -
163 - if (objCtr.defineProperty) {
164 - var classListPropDesc = {
165 - get: classListGetter,
166 - enumerable: true,
167 - configurable: true,
168 - };
169 - try {
170 - objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
171 - } catch (ex) {
172 - // IE 8 doesn't support enumerable:true
173 - // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
174 - // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
175 - if (ex.number === undefined || ex.number === -0x7ff5ec54) {
176 - classListPropDesc.enumerable = false;
177 - objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
178 - }
179 - }
180 - } else if (objCtr[protoProp].__defineGetter__) {
181 - elemCtrProto.__defineGetter__(classListProp, classListGetter);
182 - }
183 - })(window.self);
184 - }
185 -
186 - // There is full or partial native classList support, so just check if we need
187 - // to normalize the add/remove and toggle APIs.
188 -
189 - (function () {
190 - 'use strict';
191 -
192 - var testElement = document.createElement('_');
193 -
194 - testElement.classList.add('c1', 'c2');
195 -
196 - // Polyfill for IE 10/11 and Firefox <26, where classList.add and
197 - // classList.remove exist but support only one argument at a time.
198 - if (!testElement.classList.contains('c2')) {
199 - var createMethod = function (method) {
200 - var original = DOMTokenList.prototype[method];
201 -
202 - DOMTokenList.prototype[method] = function (token) {
203 - var i,
204 - len = arguments.length;
205 -
206 - for (i = 0; i < len; i++) {
207 - token = arguments[i];
208 - original.call(this, token);
209 - }
210 - };
211 - };
212 - createMethod('add');
213 - createMethod('remove');
214 - }
215 -
216 - testElement.classList.toggle('c3', false);
217 -
218 - // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
219 - // support the second argument.
220 - if (testElement.classList.contains('c3')) {
221 - var _toggle = DOMTokenList.prototype.toggle;
222 -
223 - DOMTokenList.prototype.toggle = function (token, force) {
224 - if (1 in arguments && !this.contains(token) === !force) {
225 - return force;
226 - } else {
227 - return _toggle.call(this, token);
228 - }
229 - };
230 - }
231 -
232 - testElement = null;
233 - })();
234 -}
235 -
236 -/*!
237 - * @copyright Copyright (c) 2017 IcoMoon.io
238 - * @license Licensed under MIT license
239 - * See https://github.com/Keyamoon/svgxuse
240 - * @version 1.2.6
241 - */
242 -/*jslint browser: true */
243 -/*global XDomainRequest, MutationObserver, window */
244 -(function () {
245 - 'use strict';
246 - if (typeof window !== 'undefined' && window.addEventListener) {
247 - var cache = Object.create(null); // holds xhr objects to prevent multiple requests
248 - var checkUseElems;
249 - var tid; // timeout id
250 - var debouncedCheck = function () {
251 - clearTimeout(tid);
252 - tid = setTimeout(checkUseElems, 100);
253 - };
254 - var unobserveChanges = function () {
255 - return;
256 - };
257 - var observeChanges = function () {
258 - var observer;
259 - window.addEventListener('resize', debouncedCheck, false);
260 - window.addEventListener('orientationchange', debouncedCheck, false);
261 - if (window.MutationObserver) {
262 - observer = new MutationObserver(debouncedCheck);
263 - observer.observe(document.documentElement, {
264 - childList: true,
265 - subtree: true,
266 - attributes: true,
267 - });
268 - unobserveChanges = function () {
269 - try {
270 - observer.disconnect();
271 - window.removeEventListener('resize', debouncedCheck, false);
272 - window.removeEventListener('orientationchange', debouncedCheck, false);
273 - } catch (ignore) {}
274 - };
275 - } else {
276 - document.documentElement.addEventListener('DOMSubtreeModified', debouncedCheck, false);
277 - unobserveChanges = function () {
278 - document.documentElement.removeEventListener('DOMSubtreeModified', debouncedCheck, false);
279 - window.removeEventListener('resize', debouncedCheck, false);
280 - window.removeEventListener('orientationchange', debouncedCheck, false);
281 - };
282 - }
283 - };
284 - var createRequest = function (url) {
285 - // In IE 9, cross origin requests can only be sent using XDomainRequest.
286 - // XDomainRequest would fail if CORS headers are not set.
287 - // Therefore, XDomainRequest should only be used with cross origin requests.
288 - function getOrigin(loc) {
289 - var a;
290 - if (loc.protocol !== undefined) {
291 - a = loc;
292 - } else {
293 - a = document.createElement('a');
294 - a.href = loc;
295 - }
296 - return a.protocol.replace(/:/g, '') + a.host;
297 - }
298 - var Request;
299 - var origin;
300 - var origin2;
301 - if (window.XMLHttpRequest) {
302 - Request = new XMLHttpRequest();
303 - origin = getOrigin(location);
304 - origin2 = getOrigin(url);
305 - if (Request.withCredentials === undefined && origin2 !== '' && origin2 !== origin) {
306 - Request = XDomainRequest || undefined;
307 - } else {
308 - Request = XMLHttpRequest;
309 - }
310 - }
311 - return Request;
312 - };
313 - var xlinkNS = 'http://www.w3.org/1999/xlink';
314 - checkUseElems = function () {
315 - var base;
316 - var bcr;
317 - var fallback = ''; // optional fallback URL in case no base path to SVG file was given and no symbol definition was found.
318 - var hash;
319 - var href;
320 - var i;
321 - var inProgressCount = 0;
322 - var isHidden;
323 - var Request;
324 - var url;
325 - var uses;
326 - var xhr;
327 - function observeIfDone() {
328 - // If done with making changes, start watching for chagnes in DOM again
329 - inProgressCount -= 1;
330 - if (inProgressCount === 0) {
331 - // if all xhrs were resolved
332 - unobserveChanges(); // make sure to remove old handlers
333 - observeChanges(); // watch for changes to DOM
334 - }
335 - }
336 - function attrUpdateFunc(spec) {
337 - return function () {
338 - if (cache[spec.base] !== true) {
339 - spec.useEl.setAttributeNS(xlinkNS, 'xlink:href', '#' + spec.hash);
340 - if (spec.useEl.hasAttribute('href')) {
341 - spec.useEl.setAttribute('href', '#' + spec.hash);
342 - }
343 - }
344 - };
345 - }
346 - function onloadFunc(xhr) {
347 - return function () {
348 - var body = document.body;
349 - var x = document.createElement('x');
350 - var svg;
351 - xhr.onload = null;
352 - x.innerHTML = xhr.responseText;
353 - svg = x.getElementsByTagName('svg')[0];
354 - if (svg) {
355 - svg.setAttribute('aria-hidden', 'true');
356 - svg.style.position = 'absolute';
357 - svg.style.width = 0;
358 - svg.style.height = 0;
359 - svg.style.overflow = 'hidden';
360 - body.insertBefore(svg, body.firstChild);
361 - }
362 - observeIfDone();
363 - };
364 - }
365 - function onErrorTimeout(xhr) {
366 - return function () {
367 - xhr.onerror = null;
368 - xhr.ontimeout = null;
369 - observeIfDone();
370 - };
371 - }
372 - unobserveChanges(); // stop watching for changes to DOM
373 - // find all use elements
374 - uses = document.getElementsByTagName('use');
375 - for (i = 0; i < uses.length; i += 1) {
376 - try {
377 - bcr = uses[i].getBoundingClientRect();
378 - } catch (ignore) {
379 - // failed to get bounding rectangle of the use element
380 - bcr = false;
381 - }
382 - href =
383 - uses[i].getAttribute('href') ||
384 - uses[i].getAttributeNS(xlinkNS, 'href') ||
385 - uses[i].getAttribute('xlink:href');
386 - if (href && href.split) {
387 - url = href.split('#');
388 - } else {
389 - url = ['', ''];
390 - }
391 - base = url[0];
392 - hash = url[1];
393 - isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0;
394 - if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) {
395 - // the use element is empty
396 - // if there is a reference to an external SVG, try to fetch it
397 - // use the optional fallback URL if there is no reference to an external SVG
398 - if (fallback && !base.length && hash && !document.getElementById(hash)) {
399 - base = fallback;
400 - }
401 - if (uses[i].hasAttribute('href')) {
402 - uses[i].setAttributeNS(xlinkNS, 'xlink:href', href);
403 - }
404 - if (base.length) {
405 - // schedule updating xlink:href
406 - xhr = cache[base];
407 - if (xhr !== true) {
408 - // true signifies that prepending the SVG was not required
409 - setTimeout(
410 - attrUpdateFunc({
411 - useEl: uses[i],
412 - base: base,
413 - hash: hash,
414 - }),
415 - 0
416 - );
417 - }
418 - if (xhr === undefined) {
419 - Request = createRequest(base);
420 - if (Request !== undefined) {
421 - xhr = new Request();
422 - cache[base] = xhr;
423 - xhr.onload = onloadFunc(xhr);
424 - xhr.onerror = onErrorTimeout(xhr);
425 - xhr.ontimeout = onErrorTimeout(xhr);
426 - xhr.open('GET', base);
427 - xhr.send();
428 - inProgressCount += 1;
429 - }
430 - }
431 - }
432 - } else {
433 - if (!isHidden) {
434 - if (cache[base] === undefined) {
435 - // remember this URL if the use element was not empty and no request was sent
436 - cache[base] = true;
437 - } else if (cache[base].onload) {
438 - // if it turns out that prepending the SVG is not necessary,
439 - // abort the in-progress xhr.
440 - cache[base].abort();
441 - delete cache[base].onload;
442 - cache[base] = true;
443 - }
444 - } else if (base.length && cache[base]) {
445 - setTimeout(
446 - attrUpdateFunc({
447 - useEl: uses[i],
448 - base: base,
449 - hash: hash,
450 - }),
451 - 0
452 - );
453 - }
454 - }
455 - }
456 - uses = '';
457 - inProgressCount += 1;
458 - observeIfDone();
459 - };
460 - var winLoad;
461 - winLoad = function () {
462 - window.removeEventListener('load', winLoad, false); // to prevent memory leaks
463 - tid = setTimeout(checkUseElems, 0);
464 - };
465 - if (document.readyState !== 'complete') {
466 - // The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty.
467 - window.addEventListener('load', winLoad, false);
468 - } else {
469 - // No need to add a listener if the document is already loaded, initialize immediately.
470 - winLoad();
471 - }
472 - }
473 -})();
1 -import snippet from 'tui-code-snippet';
2 -import { HELP_MENUS } from './consts';
3 -import { getSelector, assignmentForDestroy, cls } from './util';
4 -import mainContainer from './ui/template/mainContainer';
5 -import controls from './ui/template/controls';
6 -
7 -import Theme from './ui/theme/theme';
8 -import Shape from './ui/shape';
9 -import Crop from './ui/crop';
10 -import Flip from './ui/flip';
11 -import Rotate from './ui/rotate';
12 -import Text from './ui/text';
13 -import Mask from './ui/mask';
14 -import Icon from './ui/icon';
15 -import Draw from './ui/draw';
16 -import Filter from './ui/filter';
17 -import Locale from './ui/locale/locale';
18 -
19 -const SUB_UI_COMPONENT = {
20 - Shape,
21 - Crop,
22 - Flip,
23 - Rotate,
24 - Text,
25 - Mask,
26 - Icon,
27 - Draw,
28 - Filter,
29 -};
30 -
31 -const BI_EXPRESSION_MINSIZE_WHEN_TOP_POSITION = '1300';
32 -
33 -/**
34 - * Ui class
35 - * @class
36 - * @param {string|HTMLElement} element - Wrapper's element or selector
37 - * @param {Object} [options] - Ui setting options
38 - * @param {number} options.loadImage - Init default load image
39 - * @param {number} options.initMenu - Init start menu
40 - * @param {Boolean} [options.menuBarPosition=bottom] - Let
41 - * @param {Boolean} [options.applyCropSelectionStyle=false] - Let
42 - * @param {Boolean} [options.usageStatistics=false] - Use statistics or not
43 - * @param {Object} [options.uiSize] - ui size of editor
44 - * @param {string} options.uiSize.width - width of ui
45 - * @param {string} options.uiSize.height - height of ui
46 - * @param {Object} actions - ui action instance
47 - */
48 -class Ui {
49 - constructor(element, options, actions) {
50 - this.options = this._initializeOption(options);
51 - this._actions = actions;
52 - this.submenu = false;
53 - this.imageSize = {};
54 - this.uiSize = {};
55 - this._locale = new Locale(this.options.locale);
56 - this.theme = new Theme(this.options.theme);
57 - this.eventHandler = {};
58 - this._submenuChangeTransection = false;
59 - this._selectedElement = null;
60 - this._mainElement = null;
61 - this._editorElementWrap = null;
62 - this._editorElement = null;
63 - this._menuElement = null;
64 - this._subMenuElement = null;
65 - this._makeUiElement(element);
66 - this._setUiSize();
67 - this._initMenuEvent = false;
68 -
69 - this._makeSubMenu();
70 - }
71 -
72 - /**
73 - * Destroys the instance.
74 - */
75 - destroy() {
76 - this._removeUiEvent();
77 - this._destroyAllMenu();
78 - this._selectedElement.innerHTML = '';
79 -
80 - assignmentForDestroy(this);
81 - }
82 -
83 - /**
84 - * Set Default Selection for includeUI
85 - * @param {Object} option - imageEditor options
86 - * @returns {Object} - extends selectionStyle option
87 - * @ignore
88 - */
89 - setUiDefaultSelectionStyle(option) {
90 - return snippet.extend(
91 - {
92 - applyCropSelectionStyle: true,
93 - applyGroupSelectionStyle: true,
94 - selectionStyle: {
95 - cornerStyle: 'circle',
96 - cornerSize: 16,
97 - cornerColor: '#fff',
98 - cornerStrokeColor: '#fff',
99 - transparentCorners: false,
100 - lineWidth: 2,
101 - borderColor: '#fff',
102 - },
103 - },
104 - option
105 - );
106 - }
107 -
108 - /**
109 - * Change editor size
110 - * @param {Object} resizeInfo - ui & image size info
111 - * @param {Object} [resizeInfo.uiSize] - image size dimension
112 - * @param {string} resizeInfo.uiSize.width - ui width
113 - * @param {string} resizeInfo.uiSize.height - ui height
114 - * @param {Object} [resizeInfo.imageSize] - image size dimension
115 - * @param {Number} resizeInfo.imageSize.oldWidth - old width
116 - * @param {Number} resizeInfo.imageSize.oldHeight - old height
117 - * @param {Number} resizeInfo.imageSize.newWidth - new width
118 - * @param {Number} resizeInfo.imageSize.newHeight - new height
119 - * @example
120 - * // Change the image size and ui size, and change the affected ui state together.
121 - * imageEditor.ui.resizeEditor({
122 - * imageSize: {oldWidth: 100, oldHeight: 100, newWidth: 700, newHeight: 700},
123 - * uiSize: {width: 1000, height: 1000}
124 - * });
125 - * @example
126 - * // Apply the ui state while preserving the previous attribute (for example, if responsive Ui)
127 - * imageEditor.ui.resizeEditor();
128 - */
129 - resizeEditor({ uiSize, imageSize = this.imageSize } = {}) {
130 - if (imageSize !== this.imageSize) {
131 - this.imageSize = imageSize;
132 - }
133 - if (uiSize) {
134 - this._setUiSize(uiSize);
135 - }
136 -
137 - const { width, height } = this._getCanvasMaxDimension();
138 - const editorElementStyle = this._editorElement.style;
139 - const { menuBarPosition } = this.options;
140 -
141 - editorElementStyle.height = `${height}px`;
142 - editorElementStyle.width = `${width}px`;
143 -
144 - this._setEditorPosition(menuBarPosition);
145 -
146 - this._editorElementWrap.style.bottom = `0px`;
147 - this._editorElementWrap.style.top = `0px`;
148 - this._editorElementWrap.style.left = `0px`;
149 - this._editorElementWrap.style.width = `100%`;
150 -
151 - const selectElementClassList = this._selectedElement.classList;
152 -
153 - if (
154 - menuBarPosition === 'top' &&
155 - this._selectedElement.offsetWidth < BI_EXPRESSION_MINSIZE_WHEN_TOP_POSITION
156 - ) {
157 - selectElementClassList.add('tui-image-editor-top-optimization');
158 - } else {
159 - selectElementClassList.remove('tui-image-editor-top-optimization');
160 - }
161 - }
162 -
163 - /**
164 - * Change help button status
165 - * @param {string} buttonType - target button type
166 - * @param {Boolean} enableStatus - enabled status
167 - * @ignore
168 - */
169 - changeHelpButtonEnabled(buttonType, enableStatus) {
170 - const buttonClassList = this._buttonElements[buttonType].classList;
171 -
172 - buttonClassList[enableStatus ? 'add' : 'remove']('enabled');
173 - }
174 -
175 - /**
176 - * Change delete button status
177 - * @param {Object} [options] - Ui setting options
178 - * @param {object} [options.loadImage] - Init default load image
179 - * @param {string} [options.initMenu] - Init start menu
180 - * @param {string} [options.menuBarPosition=bottom] - Let
181 - * @param {boolean} [options.applyCropSelectionStyle=false] - Let
182 - * @param {boolean} [options.usageStatistics=false] - Send statistics ping or not
183 - * @returns {Object} initialize option
184 - * @private
185 - */
186 - _initializeOption(options) {
187 - return snippet.extend(
188 - {
189 - loadImage: {
190 - path: '',
191 - name: '',
192 - },
193 - locale: {},
194 - menuIconPath: '',
195 - menu: ['crop', 'flip', 'rotate', 'draw', 'shape', 'icon', 'text', 'mask', 'filter'],
196 - initMenu: '',
197 - uiSize: {
198 - width: '100%',
199 - height: '100%',
200 - },
201 - menuBarPosition: 'bottom',
202 - },
203 - options
204 - );
205 - }
206 -
207 - /**
208 - * Set ui container size
209 - * @param {Object} uiSize - ui dimension
210 - * @param {string} uiSize.width - css width property
211 - * @param {string} uiSize.height - css height property
212 - * @private
213 - */
214 - _setUiSize(uiSize = this.options.uiSize) {
215 - const elementDimension = this._selectedElement.style;
216 - elementDimension.width = uiSize.width;
217 - elementDimension.height = uiSize.height;
218 - }
219 -
220 - /**
221 - * Make submenu dom element
222 - * @private
223 - */
224 - _makeSubMenu() {
225 - snippet.forEach(this.options.menu, (menuName) => {
226 - const SubComponentClass =
227 - SUB_UI_COMPONENT[menuName.replace(/^[a-z]/, ($0) => $0.toUpperCase())];
228 -
229 - // make menu element
230 - this._makeMenuElement(menuName);
231 -
232 - // menu btn element
233 - this._buttonElements[menuName] = this._menuElement.querySelector(`.tie-btn-${menuName}`);
234 -
235 - // submenu ui instance
236 - this[menuName] = new SubComponentClass(this._subMenuElement, {
237 - locale: this._locale,
238 - makeSvgIcon: this.theme.makeMenSvgIconSet.bind(this.theme),
239 - menuBarPosition: this.options.menuBarPosition,
240 - usageStatistics: this.options.usageStatistics,
241 - });
242 - });
243 - }
244 -
245 - /**
246 - * Make primary ui dom element
247 - * @param {string|HTMLElement} element - Wrapper's element or selector
248 - * @private
249 - */
250 - _makeUiElement(element) {
251 - let selectedElement;
252 -
253 - window.snippet = snippet;
254 -
255 - if (element.nodeType) {
256 - selectedElement = element;
257 - } else {
258 - selectedElement = document.querySelector(element);
259 - }
260 - const selector = getSelector(selectedElement);
261 -
262 - selectedElement.classList.add('tui-image-editor-container');
263 - selectedElement.innerHTML =
264 - controls({
265 - locale: this._locale,
266 - biImage: this.theme.getStyle('common.bi'),
267 - loadButtonStyle: this.theme.getStyle('loadButton'),
268 - downloadButtonStyle: this.theme.getStyle('downloadButton'),
269 - }) +
270 - mainContainer({
271 - locale: this._locale,
272 - biImage: this.theme.getStyle('common.bi'),
273 - commonStyle: this.theme.getStyle('common'),
274 - headerStyle: this.theme.getStyle('header'),
275 - loadButtonStyle: this.theme.getStyle('loadButton'),
276 - downloadButtonStyle: this.theme.getStyle('downloadButton'),
277 - submenuStyle: this.theme.getStyle('submenu'),
278 - });
279 -
280 - this._selectedElement = selectedElement;
281 - this._selectedElement.classList.add(this.options.menuBarPosition);
282 -
283 - this._mainElement = selector('.tui-image-editor-main');
284 - this._editorElementWrap = selector('.tui-image-editor-wrap');
285 - this._editorElement = selector('.tui-image-editor');
286 - this._menuElement = selector('.tui-image-editor-menu');
287 - this._subMenuElement = selector('.tui-image-editor-submenu');
288 - this._buttonElements = {
289 - download: this._selectedElement.querySelectorAll('.tui-image-editor-download-btn'),
290 - load: this._selectedElement.querySelectorAll('.tui-image-editor-load-btn'),
291 - };
292 -
293 - this._addHelpMenus();
294 - }
295 -
296 - /**
297 - * make array for help menu output, including partitions.
298 - * @returns {Array}
299 - * @private
300 - */
301 - _makeHelpMenuWithPartition() {
302 - const helpMenuWithPartition = [...HELP_MENUS, ''];
303 - helpMenuWithPartition.splice(3, 0, '');
304 -
305 - return helpMenuWithPartition;
306 - }
307 -
308 - /**
309 - * Add help menu
310 - * @private
311 - */
312 - _addHelpMenus() {
313 - const helpMenuWithPartition = this._makeHelpMenuWithPartition();
314 -
315 - snippet.forEach(helpMenuWithPartition, (menuName) => {
316 - if (!menuName) {
317 - this._makeMenuPartitionElement();
318 - } else {
319 - this._makeMenuElement(menuName, ['normal', 'disabled', 'hover'], 'help');
320 -
321 - if (menuName) {
322 - this._buttonElements[menuName] = this._menuElement.querySelector(`.tie-btn-${menuName}`);
323 - }
324 - }
325 - });
326 - }
327 -
328 - /**
329 - * Make menu partition element
330 - * @private
331 - */
332 - _makeMenuPartitionElement() {
333 - const partitionElement = document.createElement('li');
334 - const partitionInnerElement = document.createElement('div');
335 - partitionElement.className = cls('item');
336 - partitionInnerElement.className = cls('icpartition');
337 - partitionElement.appendChild(partitionInnerElement);
338 -
339 - this._menuElement.appendChild(partitionElement);
340 - }
341 -
342 - /**
343 - * Make menu button element
344 - * @param {string} menuName - menu name
345 - * @param {Array} useIconTypes - Possible values are \['normal', 'active', 'hover', 'disabled'\]
346 - * @param {string} menuType - 'normal' or 'help'
347 - * @private
348 - */
349 - _makeMenuElement(menuName, useIconTypes = ['normal', 'active', 'hover'], menuType = 'normal') {
350 - const btnElement = document.createElement('li');
351 - const menuItemHtml = this.theme.makeMenSvgIconSet(useIconTypes, menuName);
352 -
353 - this._addTooltipAttribute(btnElement, menuName);
354 - btnElement.className = `tie-btn-${menuName} ${cls('item')} ${menuType}`;
355 - btnElement.innerHTML = menuItemHtml;
356 -
357 - this._menuElement.appendChild(btnElement);
358 - }
359 -
360 - /**
361 - * Add help action event
362 - * @private
363 - */
364 - _addHelpActionEvent() {
365 - snippet.forEach(HELP_MENUS, (helpName) => {
366 - this.eventHandler[helpName] = () => this._actions.main[helpName]();
367 - this._buttonElements[helpName].addEventListener('click', this.eventHandler[helpName]);
368 - });
369 - }
370 -
371 - /**
372 - * Remove help action event
373 - * @private
374 - */
375 - _removeHelpActionEvent() {
376 - snippet.forEach(HELP_MENUS, (helpName) => {
377 - this._buttonElements[helpName].removeEventListener('click', this.eventHandler[helpName]);
378 - });
379 - }
380 -
381 - /**
382 - * Add attribute for menu tooltip
383 - * @param {HTMLElement} element - menu element
384 - * @param {string} tooltipName - tooltipName
385 - * @private
386 - */
387 - _addTooltipAttribute(element, tooltipName) {
388 - element.setAttribute(
389 - 'tooltip-content',
390 - this._locale.localize(tooltipName.replace(/^[a-z]/g, ($0) => $0.toUpperCase()))
391 - );
392 - }
393 -
394 - /**
395 - * Add download event
396 - * @private
397 - */
398 - _addDownloadEvent() {
399 - this.eventHandler.download = () => this._actions.main.download();
400 - snippet.forEach(this._buttonElements.download, (element) => {
401 - element.addEventListener('click', this.eventHandler.download);
402 - });
403 - }
404 -
405 - _removeDownloadEvent() {
406 - snippet.forEach(this._buttonElements.download, (element) => {
407 - element.removeEventListener('click', this.eventHandler.download);
408 - });
409 - }
410 -
411 - /**
412 - * Add load event
413 - * @private
414 - */
415 - _addLoadEvent() {
416 - this.eventHandler.loadImage = (event) => this._actions.main.load(event.target.files[0]);
417 -
418 - snippet.forEach(this._buttonElements.load, (element) => {
419 - element.addEventListener('change', this.eventHandler.loadImage);
420 - });
421 - }
422 -
423 - /**
424 - * Remmove load event
425 - * @private
426 - */
427 - _removeLoadEvent() {
428 - snippet.forEach(this._buttonElements.load, (element) => {
429 - element.removeEventListener('change', this.eventHandler.loadImage);
430 - });
431 - }
432 -
433 - /**
434 - * Add menu event
435 - * @param {string} menuName - menu name
436 - * @private
437 - */
438 - _addMainMenuEvent(menuName) {
439 - this.eventHandler[menuName] = () => this.changeMenu(menuName);
440 - this._buttonElements[menuName].addEventListener('click', this.eventHandler[menuName]);
441 - }
442 -
443 - /**
444 - * Add menu event
445 - * @param {string} menuName - menu name
446 - * @private
447 - */
448 - _addSubMenuEvent(menuName) {
449 - this[menuName].addEvent(this._actions[menuName]);
450 - }
451 -
452 - /**
453 - * Add menu event
454 - * @private
455 - */
456 - _addMenuEvent() {
457 - snippet.forEach(this.options.menu, (menuName) => {
458 - this._addMainMenuEvent(menuName);
459 - this._addSubMenuEvent(menuName);
460 - });
461 - }
462 -
463 - /**
464 - * Remove menu event
465 - * @private
466 - */
467 - _removeMainMenuEvent() {
468 - snippet.forEach(this.options.menu, (menuName) => {
469 - this._buttonElements[menuName].removeEventListener('click', this.eventHandler[menuName]);
470 - });
471 - }
472 -
473 - /**
474 - * Get editor area element
475 - * @returns {HTMLElement} editor area html element
476 - * @ignore
477 - */
478 - getEditorArea() {
479 - return this._editorElement;
480 - }
481 -
482 - /**
483 - * Add event for menu items
484 - * @ignore
485 - */
486 - activeMenuEvent() {
487 - if (this._initMenuEvent) {
488 - return;
489 - }
490 -
491 - this._addHelpActionEvent();
492 - this._addDownloadEvent();
493 - this._addMenuEvent();
494 - this._initMenu();
495 - this._initMenuEvent = true;
496 - }
497 -
498 - /**
499 - * Remove ui event
500 - * @private
501 - */
502 - _removeUiEvent() {
503 - this._removeHelpActionEvent();
504 - this._removeDownloadEvent();
505 - this._removeLoadEvent();
506 - this._removeMainMenuEvent();
507 - }
508 -
509 - /**
510 - * Destroy all menu instance
511 - * @private
512 - */
513 - _destroyAllMenu() {
514 - snippet.forEach(this.options.menu, (menuName) => {
515 - this[menuName].destroy();
516 - });
517 - }
518 -
519 - /**
520 - * Init canvas
521 - * @ignore
522 - */
523 - initCanvas() {
524 - const loadImageInfo = this._getLoadImage();
525 - if (loadImageInfo.path) {
526 - this._actions.main.initLoadImage(loadImageInfo.path, loadImageInfo.name).then(() => {
527 - this.activeMenuEvent();
528 - });
529 - }
530 -
531 - this._addLoadEvent();
532 -
533 - const gridVisual = document.createElement('div');
534 -
535 - gridVisual.className = cls('grid-visual');
536 - const grid = `<table>
537 - <tr><td class="dot left-top"></td><td></td><td class="dot right-top"></td></tr>
538 - <tr><td></td><td></td><td></td></tr>
539 - <tr><td class="dot left-bottom"></td><td></td><td class="dot right-bottom"></td></tr>
540 - </table>`;
541 - gridVisual.innerHTML = grid;
542 - this._editorContainerElement = this._editorElement.querySelector(
543 - '.tui-image-editor-canvas-container'
544 - );
545 - this._editorContainerElement.appendChild(gridVisual);
546 - }
547 -
548 - /**
549 - * get editor area element
550 - * @returns {Object} load image option
551 - * @private
552 - */
553 - _getLoadImage() {
554 - return this.options.loadImage;
555 - }
556 -
557 - /**
558 - * change menu
559 - * @param {string} menuName - menu name
560 - * @param {boolean} toggle - whether toogle or not
561 - * @param {boolean} discardSelection - discard selection
562 - * @ignore
563 - */
564 - changeMenu(menuName, toggle = true, discardSelection = true) {
565 - if (!this._submenuChangeTransection) {
566 - this._submenuChangeTransection = true;
567 - this._changeMenu(menuName, toggle, discardSelection);
568 - this._submenuChangeTransection = false;
569 - }
570 - }
571 -
572 - /**
573 - * change menu
574 - * @param {string} menuName - menu name
575 - * @param {boolean} toggle - whether toogle or not
576 - * @param {boolean} discardSelection - discard selection
577 - * @private
578 - */
579 - _changeMenu(menuName, toggle, discardSelection) {
580 - if (this.submenu) {
581 - this._buttonElements[this.submenu].classList.remove('active');
582 - this._mainElement.classList.remove(`tui-image-editor-menu-${this.submenu}`);
583 - if (discardSelection) {
584 - this._actions.main.discardSelection();
585 - }
586 - this._actions.main.changeSelectableAll(true);
587 - this[this.submenu].changeStandbyMode();
588 - }
589 -
590 - if (this.submenu === menuName && toggle) {
591 - this.submenu = null;
592 - } else {
593 - this._buttonElements[menuName].classList.add('active');
594 - this._mainElement.classList.add(`tui-image-editor-menu-${menuName}`);
595 - this.submenu = menuName;
596 - this[this.submenu].changeStartMode();
597 - }
598 -
599 - this.resizeEditor();
600 - }
601 -
602 - /**
603 - * Init menu
604 - * @private
605 - */
606 - _initMenu() {
607 - if (this.options.initMenu) {
608 - const evt = document.createEvent('MouseEvents');
609 - evt.initEvent('click', true, false);
610 - this._buttonElements[this.options.initMenu].dispatchEvent(evt);
611 - }
612 -
613 - if (this.icon) {
614 - this.icon.registDefaultIcon();
615 - }
616 - }
617 -
618 - /**
619 - * Get canvas max Dimension
620 - * @returns {Object} - width & height of editor
621 - * @private
622 - */
623 - _getCanvasMaxDimension() {
624 - const { maxWidth, maxHeight } = this._editorContainerElement.style;
625 - const width = parseFloat(maxWidth);
626 - const height = parseFloat(maxHeight);
627 -
628 - return {
629 - width,
630 - height,
631 - };
632 - }
633 -
634 - /**
635 - * Set editor position
636 - * @param {string} menuBarPosition - top or right or bottom or left
637 - * @private
638 - */
639 - // eslint-disable-next-line complexity
640 - _setEditorPosition(menuBarPosition) {
641 - const { width, height } = this._getCanvasMaxDimension();
642 - const editorElementStyle = this._editorElement.style;
643 - let top = 0;
644 - let left = 0;
645 -
646 - if (this.submenu) {
647 - if (menuBarPosition === 'bottom') {
648 - if (height > this._editorElementWrap.scrollHeight - 150) {
649 - top = (height - this._editorElementWrap.scrollHeight) / 2;
650 - } else {
651 - top = (150 / 2) * -1;
652 - }
653 - } else if (menuBarPosition === 'top') {
654 - if (height > this._editorElementWrap.offsetHeight - 150) {
655 - top = 150 / 2 - (height - (this._editorElementWrap.offsetHeight - 150)) / 2;
656 - } else {
657 - top = 150 / 2;
658 - }
659 - } else if (menuBarPosition === 'left') {
660 - if (width > this._editorElementWrap.offsetWidth - 248) {
661 - left = 248 / 2 - (width - (this._editorElementWrap.offsetWidth - 248)) / 2;
662 - } else {
663 - left = 248 / 2;
664 - }
665 - } else if (menuBarPosition === 'right') {
666 - if (width > this._editorElementWrap.scrollWidth - 248) {
667 - left = (width - this._editorElementWrap.scrollWidth) / 2;
668 - } else {
669 - left = (248 / 2) * -1;
670 - }
671 - }
672 - }
673 - editorElementStyle.top = `${top}px`;
674 - editorElementStyle.left = `${left}px`;
675 - }
676 -}
677 -
678 -export default Ui;
1 -import snippet from 'tui-code-snippet';
2 -import Submenu from './submenuBase';
3 -import { assignmentForDestroy } from '../util';
4 -import templateHtml from './template/submenu/crop';
5 -
6 -/**
7 - * Crop ui class
8 - * @class
9 - * @ignore
10 - */
11 -class Crop extends Submenu {
12 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
13 - super(subMenuElement, {
14 - locale,
15 - name: 'crop',
16 - makeSvgIcon,
17 - menuBarPosition,
18 - templateHtml,
19 - usageStatistics,
20 - });
21 -
22 - this.status = 'active';
23 -
24 - this._els = {
25 - apply: this.selector('.tie-crop-button .apply'),
26 - cancel: this.selector('.tie-crop-button .cancel'),
27 - preset: this.selector('.tie-crop-preset-button'),
28 - };
29 -
30 - this.defaultPresetButton = this._els.preset.querySelector('.preset-none');
31 - }
32 -
33 - /**
34 - * Destroys the instance.
35 - */
36 - destroy() {
37 - this._removeEvent();
38 -
39 - assignmentForDestroy(this);
40 - }
41 -
42 - /**
43 - * Add event for crop
44 - * @param {Object} actions - actions for crop
45 - * @param {Function} actions.crop - crop action
46 - * @param {Function} actions.cancel - cancel action
47 - * @param {Function} actions.preset - draw rectzone at a predefined ratio
48 - */
49 - addEvent(actions) {
50 - const apply = this._applyEventHandler.bind(this);
51 - const cancel = this._cancelEventHandler.bind(this);
52 - const cropzonePreset = this._cropzonePresetEventHandler.bind(this);
53 -
54 - this.eventHandler = {
55 - apply,
56 - cancel,
57 - cropzonePreset,
58 - };
59 -
60 - this.actions = actions;
61 - this._els.apply.addEventListener('click', apply);
62 - this._els.cancel.addEventListener('click', cancel);
63 - this._els.preset.addEventListener('click', cropzonePreset);
64 - }
65 -
66 - /**
67 - * Remove event
68 - * @private
69 - */
70 - _removeEvent() {
71 - this._els.apply.removeEventListener('click', this.eventHandler.apply);
72 - this._els.cancel.removeEventListener('click', this.eventHandler.cancel);
73 - this._els.preset.removeEventListener('click', this.eventHandler.cropzonePreset);
74 - }
75 -
76 - _applyEventHandler() {
77 - this.actions.crop();
78 - this._els.apply.classList.remove('active');
79 - }
80 -
81 - _cancelEventHandler() {
82 - this.actions.cancel();
83 - this._els.apply.classList.remove('active');
84 - }
85 -
86 - _cropzonePresetEventHandler(event) {
87 - const button = event.target.closest('.tui-image-editor-button.preset');
88 - if (button) {
89 - const [presetType] = button.className.match(/preset-[^\s]+/);
90 -
91 - this._setPresetButtonActive(button);
92 - this.actions.preset(presetType);
93 - }
94 - }
95 -
96 - /**
97 - * Executed when the menu starts.
98 - */
99 - changeStartMode() {
100 - this.actions.modeChange('crop');
101 - }
102 -
103 - /**
104 - * Returns the menu to its default state.
105 - */
106 - changeStandbyMode() {
107 - this.actions.stopDrawingMode();
108 - this._setPresetButtonActive();
109 - }
110 -
111 - /**
112 - * Change apply button status
113 - * @param {Boolean} enableStatus - apply button status
114 - */
115 - changeApplyButtonStatus(enableStatus) {
116 - if (enableStatus) {
117 - this._els.apply.classList.add('active');
118 - } else {
119 - this._els.apply.classList.remove('active');
120 - }
121 - }
122 -
123 - /**
124 - * Set preset button to active status
125 - * @param {HTMLElement} button - event target element
126 - * @private
127 - */
128 - _setPresetButtonActive(button = this.defaultPresetButton) {
129 - snippet.forEach([].slice.call(this._els.preset.querySelectorAll('.preset')), (presetButton) => {
130 - presetButton.classList.remove('active');
131 - });
132 -
133 - if (button) {
134 - button.classList.add('active');
135 - }
136 - }
137 -}
138 -
139 -export default Crop;
1 -import { assignmentForDestroy, getRgb } from '../util';
2 -import Colorpicker from './tools/colorpicker';
3 -import Range from './tools/range';
4 -import Submenu from './submenuBase';
5 -import templateHtml from './template/submenu/draw';
6 -import { defaultDrawRangeValus } from '../consts';
7 -const DRAW_OPACITY = 0.7;
8 -
9 -/**
10 - * Draw ui class
11 - * @class
12 - * @ignore
13 - */
14 -class Draw extends Submenu {
15 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
16 - super(subMenuElement, {
17 - locale,
18 - name: 'draw',
19 - makeSvgIcon,
20 - menuBarPosition,
21 - templateHtml,
22 - usageStatistics,
23 - });
24 -
25 - this._els = {
26 - lineSelectButton: this.selector('.tie-draw-line-select-button'),
27 - drawColorPicker: new Colorpicker(
28 - this.selector('.tie-draw-color'),
29 - '#00a9ff',
30 - this.toggleDirection,
31 - this.usageStatistics
32 - ),
33 - drawRange: new Range(
34 - {
35 - slider: this.selector('.tie-draw-range'),
36 - input: this.selector('.tie-draw-range-value'),
37 - },
38 - defaultDrawRangeValus
39 - ),
40 - };
41 -
42 - this.type = null;
43 - this.color = this._els.drawColorPicker.color;
44 - this.width = this._els.drawRange.value;
45 - }
46 -
47 - /**
48 - * Destroys the instance.
49 - */
50 - destroy() {
51 - this._removeEvent();
52 - this._els.drawColorPicker.destroy();
53 - this._els.drawRange.destroy();
54 -
55 - assignmentForDestroy(this);
56 - }
57 -
58 - /**
59 - * Add event for draw
60 - * @param {Object} actions - actions for crop
61 - * @param {Function} actions.setDrawMode - set draw mode
62 - */
63 - addEvent(actions) {
64 - this.eventHandler.changeDrawType = this._changeDrawType.bind(this);
65 -
66 - this.actions = actions;
67 - this._els.lineSelectButton.addEventListener('click', this.eventHandler.changeDrawType);
68 - this._els.drawColorPicker.on('change', this._changeDrawColor.bind(this));
69 - this._els.drawRange.on('change', this._changeDrawRange.bind(this));
70 - }
71 -
72 - /**
73 - * Remove event
74 - * @private
75 - */
76 - _removeEvent() {
77 - this._els.lineSelectButton.removeEventListener('click', this.eventHandler.changeDrawType);
78 - this._els.drawColorPicker.off();
79 - this._els.drawRange.off();
80 - }
81 -
82 - /**
83 - * set draw mode - action runner
84 - */
85 - setDrawMode() {
86 - this.actions.setDrawMode(this.type, {
87 - width: this.width,
88 - color: getRgb(this.color, DRAW_OPACITY),
89 - });
90 - }
91 -
92 - /**
93 - * Returns the menu to its default state.
94 - */
95 - changeStandbyMode() {
96 - this.type = null;
97 - this.actions.stopDrawingMode();
98 - this.actions.changeSelectableAll(true);
99 - this._els.lineSelectButton.classList.remove('free');
100 - this._els.lineSelectButton.classList.remove('line');
101 - }
102 -
103 - /**
104 - * Executed when the menu starts.
105 - */
106 - changeStartMode() {
107 - this.type = 'free';
108 - this._els.lineSelectButton.classList.add('free');
109 - this.setDrawMode();
110 - }
111 -
112 - /**
113 - * Change draw type event
114 - * @param {object} event - line select event
115 - * @private
116 - */
117 - _changeDrawType(event) {
118 - const button = event.target.closest('.tui-image-editor-button');
119 - if (button) {
120 - const lineType = this.getButtonType(button, ['free', 'line']);
121 - this.actions.discardSelection();
122 -
123 - if (this.type === lineType) {
124 - this.changeStandbyMode();
125 -
126 - return;
127 - }
128 -
129 - this.changeStandbyMode();
130 - this.type = lineType;
131 - this._els.lineSelectButton.classList.add(lineType);
132 - this.setDrawMode();
133 - }
134 - }
135 -
136 - /**
137 - * Change drawing color
138 - * @param {string} color - select drawing color
139 - * @private
140 - */
141 - _changeDrawColor(color) {
142 - this.color = color || 'transparent';
143 - if (!this.type) {
144 - this.changeStartMode();
145 - } else {
146 - this.setDrawMode();
147 - }
148 - }
149 -
150 - /**
151 - * Change drawing Range
152 - * @param {number} value - select drawing range
153 - * @private
154 - */
155 - _changeDrawRange(value) {
156 - this.width = value;
157 - if (!this.type) {
158 - this.changeStartMode();
159 - } else {
160 - this.setDrawMode();
161 - }
162 - }
163 -}
164 -
165 -export default Draw;
1 -import snippet from 'tui-code-snippet';
2 -import Colorpicker from './tools/colorpicker';
3 -import Range from './tools/range';
4 -import Submenu from './submenuBase';
5 -import templateHtml from './template/submenu/filter';
6 -import { toInteger, toCamelCase, assignmentForDestroy } from '../util';
7 -import { defaultFilterRangeValus as FILTER_RANGE } from '../consts';
8 -
9 -const PICKER_CONTROL_HEIGHT = '130px';
10 -const BLEND_OPTIONS = ['add', 'diff', 'subtract', 'multiply', 'screen', 'lighten', 'darken'];
11 -const FILTER_OPTIONS = [
12 - 'grayscale',
13 - 'invert',
14 - 'sepia',
15 - 'vintage',
16 - 'blur',
17 - 'sharpen',
18 - 'emboss',
19 - 'remove-white',
20 - 'brightness',
21 - 'noise',
22 - 'pixelate',
23 - 'color-filter',
24 - 'tint',
25 - 'multiply',
26 - 'blend',
27 -];
28 -
29 -const filterNameMap = {
30 - grayscale: 'grayscale',
31 - invert: 'invert',
32 - sepia: 'sepia',
33 - blur: 'blur',
34 - sharpen: 'sharpen',
35 - emboss: 'emboss',
36 - removeWhite: 'removeColor',
37 - brightness: 'brightness',
38 - contrast: 'contrast',
39 - saturation: 'saturation',
40 - vintage: 'vintage',
41 - polaroid: 'polaroid',
42 - noise: 'noise',
43 - pixelate: 'pixelate',
44 - colorFilter: 'removeColor',
45 - tint: 'blendColor',
46 - multiply: 'blendColor',
47 - blend: 'blendColor',
48 - hue: 'hue',
49 - gamma: 'gamma',
50 -};
51 -
52 -const RANGE_INSTANCE_NAMES = [
53 - 'removewhiteDistanceRange',
54 - 'colorfilterThresholeRange',
55 - 'pixelateRange',
56 - 'noiseRange',
57 - 'brightnessRange',
58 - 'tintOpacity',
59 -];
60 -const COLORPICKER_INSTANCE_NAMES = ['filterBlendColor', 'filterMultiplyColor', 'filterTintColor'];
61 -
62 -/**
63 - * Filter ui class
64 - * @class
65 - * @ignore
66 - */
67 -class Filter extends Submenu {
68 - constructor(subMenuElement, { locale, menuBarPosition, usageStatistics }) {
69 - super(subMenuElement, {
70 - locale,
71 - name: 'filter',
72 - menuBarPosition,
73 - templateHtml,
74 - usageStatistics,
75 - });
76 -
77 - this.selectBoxShow = false;
78 -
79 - this.checkedMap = {};
80 - this._makeControlElement();
81 - }
82 -
83 - /**
84 - * Destroys the instance.
85 - */
86 - destroy() {
87 - this._removeEvent();
88 - this._destroyToolInstance();
89 -
90 - assignmentForDestroy(this);
91 - }
92 -
93 - /**
94 - * Remove event for filter
95 - */
96 - _removeEvent() {
97 - snippet.forEach(FILTER_OPTIONS, (filter) => {
98 - const filterCheckElement = this.selector(`.tie-${filter}`);
99 - const filterNameCamelCase = toCamelCase(filter);
100 -
101 - filterCheckElement.removeEventListener('change', this.eventHandler[filterNameCamelCase]);
102 - });
103 -
104 - snippet.forEach([...RANGE_INSTANCE_NAMES, ...COLORPICKER_INSTANCE_NAMES], (instanceName) => {
105 - this._els[instanceName].off();
106 - });
107 -
108 - this._els.blendType.removeEventListener('change', this.eventHandler.changeBlendFilter);
109 - this._els.blendType.removeEventListener('click', this.eventHandler.changeBlendFilter);
110 - }
111 -
112 - _destroyToolInstance() {
113 - snippet.forEach([...RANGE_INSTANCE_NAMES, ...COLORPICKER_INSTANCE_NAMES], (instanceName) => {
114 - this._els[instanceName].destroy();
115 - });
116 - }
117 -
118 - /**
119 - * Add event for filter
120 - * @param {Object} actions - actions for crop
121 - * @param {Function} actions.applyFilter - apply filter option
122 - */
123 - addEvent({ applyFilter }) {
124 - const changeFilterState = (filterName) =>
125 - this._changeFilterState.bind(this, applyFilter, filterName);
126 - const changeFilterStateForRange = (filterName) => (value, isLast) =>
127 - this._changeFilterState(applyFilter, filterName, isLast);
128 -
129 - this.eventHandler = {
130 - changeBlendFilter: changeFilterState('blend'),
131 - blandTypeClick: (event) => event.stopPropagation(),
132 - };
133 -
134 - snippet.forEach(FILTER_OPTIONS, (filter) => {
135 - const filterCheckElement = this.selector(`.tie-${filter}`);
136 - const filterNameCamelCase = toCamelCase(filter);
137 - this.checkedMap[filterNameCamelCase] = filterCheckElement;
138 - this.eventHandler[filterNameCamelCase] = changeFilterState(filterNameCamelCase);
139 -
140 - filterCheckElement.addEventListener('change', this.eventHandler[filterNameCamelCase]);
141 - });
142 -
143 - this._els.removewhiteDistanceRange.on('change', changeFilterStateForRange('removeWhite'));
144 - this._els.colorfilterThresholeRange.on('change', changeFilterStateForRange('colorFilter'));
145 - this._els.pixelateRange.on('change', changeFilterStateForRange('pixelate'));
146 - this._els.noiseRange.on('change', changeFilterStateForRange('noise'));
147 - this._els.brightnessRange.on('change', changeFilterStateForRange('brightness'));
148 -
149 - this._els.filterBlendColor.on('change', this.eventHandler.changeBlendFilter);
150 - this._els.filterMultiplyColor.on('change', changeFilterState('multiply'));
151 - this._els.filterTintColor.on('change', changeFilterState('tint'));
152 - this._els.tintOpacity.on('change', changeFilterStateForRange('tint'));
153 - this._els.filterMultiplyColor.on('changeShow', this.colorPickerChangeShow.bind(this));
154 - this._els.filterTintColor.on('changeShow', this.colorPickerChangeShow.bind(this));
155 - this._els.filterBlendColor.on('changeShow', this.colorPickerChangeShow.bind(this));
156 -
157 - this._els.blendType.addEventListener('change', this.eventHandler.changeBlendFilter);
158 - this._els.blendType.addEventListener('click', this.eventHandler.blandTypeClick);
159 - }
160 -
161 - /**
162 - * Set filter for undo changed
163 - * @param {Object} chagedFilterInfos - changed command infos
164 - * @param {string} type - filter type
165 - * @param {string} action - add or remove
166 - * @param {Object} options - filter options
167 - */
168 - setFilterState(chagedFilterInfos) {
169 - const { type, options, action } = chagedFilterInfos;
170 - const filterName = this._getFilterNameFromOptions(type, options);
171 - const isRemove = action === 'remove';
172 -
173 - if (!isRemove) {
174 - this._setFilterState(filterName, options);
175 - }
176 -
177 - this.checkedMap[filterName].checked = !isRemove;
178 - }
179 -
180 - /**
181 - * Set filter for undo changed
182 - * @param {string} filterName - filter name
183 - * @param {Object} options - filter options
184 - * @private
185 - */
186 - // eslint-disable-next-line complexity
187 - _setFilterState(filterName, options) {
188 - if (filterName === 'colorFilter') {
189 - this._els.colorfilterThresholeRange.value = options.distance;
190 - } else if (filterName === 'removeWhite') {
191 - this._els.removewhiteDistanceRange.value = options.distance;
192 - } else if (filterName === 'pixelate') {
193 - this._els.pixelateRange.value = options.blocksize;
194 - } else if (filterName === 'brightness') {
195 - this._els.brightnessRange.value = options.brightness;
196 - } else if (filterName === 'noise') {
197 - this._els.noiseRange.value = options.noise;
198 - } else if (filterName === 'tint') {
199 - this._els.tintOpacity.value = options.alpha;
200 - this._els.filterTintColor.color = options.color;
201 - } else if (filterName === 'blend') {
202 - this._els.filterBlendColor.color = options.color;
203 - } else if (filterName === 'multiply') {
204 - this._els.filterMultiplyColor.color = options.color;
205 - }
206 - }
207 -
208 - /**
209 - * Get filter name
210 - * @param {string} type - filter type
211 - * @param {Object} options - filter options
212 - * @returns {string} filter name
213 - * @private
214 - */
215 - _getFilterNameFromOptions(type, options) {
216 - let filterName = type;
217 -
218 - if (type === 'removeColor') {
219 - filterName = snippet.isExisty(options.useAlpha) ? 'removeWhite' : 'colorFilter';
220 - } else if (type === 'blendColor') {
221 - filterName = {
222 - add: 'blend',
223 - multiply: 'multiply',
224 - tint: 'tint',
225 - }[options.mode];
226 - }
227 -
228 - return filterName;
229 - }
230 -
231 - /**
232 - * Add event for filter
233 - * @param {Function} applyFilter - actions for firter
234 - * @param {string} filterName - filter name
235 - * @param {boolean} [isLast] - Is last change
236 - */
237 - _changeFilterState(applyFilter, filterName, isLast = true) {
238 - const apply = this.checkedMap[filterName].checked;
239 - const type = filterNameMap[filterName];
240 -
241 - const checkboxGroup = this.checkedMap[filterName].closest('.tui-image-editor-checkbox-group');
242 - if (checkboxGroup) {
243 - if (apply) {
244 - checkboxGroup.classList.remove('tui-image-editor-disabled');
245 - } else {
246 - checkboxGroup.classList.add('tui-image-editor-disabled');
247 - }
248 - }
249 - applyFilter(apply, type, this._getFilterOption(filterName), !isLast);
250 - }
251 -
252 - /**
253 - * Get filter option
254 - * @param {String} type - filter type
255 - * @returns {Object} filter option object
256 - * @private
257 - */
258 - // eslint-disable-next-line complexity
259 - _getFilterOption(type) {
260 - const option = {};
261 - switch (type) {
262 - case 'removeWhite':
263 - option.color = '#FFFFFF';
264 - option.useAlpha = false;
265 - option.distance = parseFloat(this._els.removewhiteDistanceRange.value);
266 - break;
267 - case 'colorFilter':
268 - option.color = '#FFFFFF';
269 - option.distance = parseFloat(this._els.colorfilterThresholeRange.value);
270 - break;
271 - case 'pixelate':
272 - option.blocksize = toInteger(this._els.pixelateRange.value);
273 - break;
274 - case 'noise':
275 - option.noise = toInteger(this._els.noiseRange.value);
276 - break;
277 - case 'brightness':
278 - option.brightness = parseFloat(this._els.brightnessRange.value);
279 - break;
280 - case 'blend':
281 - option.mode = 'add';
282 - option.color = this._els.filterBlendColor.color;
283 - option.mode = this._els.blendType.value;
284 - break;
285 - case 'multiply':
286 - option.mode = 'multiply';
287 - option.color = this._els.filterMultiplyColor.color;
288 - break;
289 - case 'tint':
290 - option.mode = 'tint';
291 - option.color = this._els.filterTintColor.color;
292 - option.alpha = this._els.tintOpacity.value;
293 - break;
294 - case 'blur':
295 - option.blur = this._els.blurRange.value;
296 - break;
297 - default:
298 - break;
299 - }
300 -
301 - return option;
302 - }
303 -
304 - /**
305 - * Make submenu range and colorpicker control
306 - * @private
307 - */
308 - _makeControlElement() {
309 - this._els = {
310 - removewhiteDistanceRange: new Range(
311 - { slider: this.selector('.tie-removewhite-distance-range') },
312 - FILTER_RANGE.removewhiteDistanceRange
313 - ),
314 - brightnessRange: new Range(
315 - { slider: this.selector('.tie-brightness-range') },
316 - FILTER_RANGE.brightnessRange
317 - ),
318 - noiseRange: new Range({ slider: this.selector('.tie-noise-range') }, FILTER_RANGE.noiseRange),
319 - pixelateRange: new Range(
320 - { slider: this.selector('.tie-pixelate-range') },
321 - FILTER_RANGE.pixelateRange
322 - ),
323 - colorfilterThresholeRange: new Range(
324 - { slider: this.selector('.tie-colorfilter-threshole-range') },
325 - FILTER_RANGE.colorfilterThresholeRange
326 - ),
327 - filterTintColor: new Colorpicker(
328 - this.selector('.tie-filter-tint-color'),
329 - '#03bd9e',
330 - this.toggleDirection,
331 - this.usageStatistics
332 - ),
333 - filterMultiplyColor: new Colorpicker(
334 - this.selector('.tie-filter-multiply-color'),
335 - '#515ce6',
336 - this.toggleDirection,
337 - this.usageStatistics
338 - ),
339 - filterBlendColor: new Colorpicker(
340 - this.selector('.tie-filter-blend-color'),
341 - '#ffbb3b',
342 - this.toggleDirection,
343 - this.usageStatistics
344 - ),
345 - blurRange: FILTER_RANGE.blurFilterRange,
346 - };
347 -
348 - this._els.tintOpacity = this._pickerWithRange(this._els.filterTintColor.pickerControl);
349 - this._els.blendType = this._pickerWithSelectbox(this._els.filterBlendColor.pickerControl);
350 -
351 - this.colorPickerControls.push(this._els.filterTintColor);
352 - this.colorPickerControls.push(this._els.filterMultiplyColor);
353 - this.colorPickerControls.push(this._els.filterBlendColor);
354 - }
355 -
356 - /**
357 - * Make submenu control for picker & range mixin
358 - * @param {HTMLElement} pickerControl - pickerControl dom element
359 - * @returns {Range}
360 - * @private
361 - */
362 - _pickerWithRange(pickerControl) {
363 - const rangeWrap = document.createElement('div');
364 - const rangelabel = document.createElement('label');
365 - const slider = document.createElement('div');
366 -
367 - slider.id = 'tie-filter-tint-opacity';
368 - rangelabel.innerHTML = 'Opacity';
369 - rangeWrap.appendChild(rangelabel);
370 - rangeWrap.appendChild(slider);
371 - pickerControl.appendChild(rangeWrap);
372 - pickerControl.style.height = PICKER_CONTROL_HEIGHT;
373 -
374 - return new Range({ slider }, FILTER_RANGE.tintOpacityRange);
375 - }
376 -
377 - /**
378 - * Make submenu control for picker & selectbox
379 - * @param {HTMLElement} pickerControl - pickerControl dom element
380 - * @returns {HTMLElement}
381 - * @private
382 - */
383 - _pickerWithSelectbox(pickerControl) {
384 - const selectlistWrap = document.createElement('div');
385 - const selectlist = document.createElement('select');
386 - const optionlist = document.createElement('ul');
387 -
388 - selectlistWrap.className = 'tui-image-editor-selectlist-wrap';
389 - optionlist.className = 'tui-image-editor-selectlist';
390 -
391 - selectlistWrap.appendChild(selectlist);
392 - selectlistWrap.appendChild(optionlist);
393 -
394 - this._makeSelectOptionList(selectlist);
395 -
396 - pickerControl.appendChild(selectlistWrap);
397 - pickerControl.style.height = PICKER_CONTROL_HEIGHT;
398 -
399 - this._drawSelectOptionList(selectlist, optionlist);
400 - this._pickerWithSelectboxForAddEvent(selectlist, optionlist);
401 -
402 - return selectlist;
403 - }
404 -
405 - /**
406 - * Make selectbox option list custom style
407 - * @param {HTMLElement} selectlist - selectbox element
408 - * @param {HTMLElement} optionlist - custom option list item element
409 - * @private
410 - */
411 - _drawSelectOptionList(selectlist, optionlist) {
412 - const options = selectlist.querySelectorAll('option');
413 - snippet.forEach(options, (option) => {
414 - const optionElement = document.createElement('li');
415 - optionElement.innerHTML = option.innerHTML;
416 - optionElement.setAttribute('data-item', option.value);
417 - optionlist.appendChild(optionElement);
418 - });
419 - }
420 -
421 - /**
422 - * custome selectbox custom event
423 - * @param {HTMLElement} selectlist - selectbox element
424 - * @param {HTMLElement} optionlist - custom option list item element
425 - * @private
426 - */
427 - _pickerWithSelectboxForAddEvent(selectlist, optionlist) {
428 - optionlist.addEventListener('click', (event) => {
429 - const optionValue = event.target.getAttribute('data-item');
430 - const fireEvent = document.createEvent('HTMLEvents');
431 -
432 - selectlist.querySelector(`[value="${optionValue}"]`).selected = true;
433 - fireEvent.initEvent('change', true, true);
434 -
435 - selectlist.dispatchEvent(fireEvent);
436 -
437 - this.selectBoxShow = false;
438 - optionlist.style.display = 'none';
439 - });
440 -
441 - selectlist.addEventListener('mousedown', (event) => {
442 - event.preventDefault();
443 - this.selectBoxShow = !this.selectBoxShow;
444 - optionlist.style.display = this.selectBoxShow ? 'block' : 'none';
445 - optionlist.setAttribute('data-selectitem', selectlist.value);
446 - optionlist.querySelector(`[data-item='${selectlist.value}']`).classList.add('active');
447 - });
448 - }
449 -
450 - /**
451 - * Make option list for select control
452 - * @param {HTMLElement} selectlist - blend option select list element
453 - * @private
454 - */
455 - _makeSelectOptionList(selectlist) {
456 - snippet.forEach(BLEND_OPTIONS, (option) => {
457 - const selectOption = document.createElement('option');
458 - selectOption.setAttribute('value', option);
459 - selectOption.innerHTML = option.replace(/^[a-z]/, ($0) => $0.toUpperCase());
460 - selectlist.appendChild(selectOption);
461 - });
462 - }
463 -}
464 -
465 -export default Filter;
1 -import snippet from 'tui-code-snippet';
2 -import { assignmentForDestroy } from '../util';
3 -import Submenu from './submenuBase';
4 -import templateHtml from './template/submenu/flip';
5 -
6 -/**
7 - * Flip ui class
8 - * @class
9 - * @ignore
10 - */
11 -class Flip extends Submenu {
12 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
13 - super(subMenuElement, {
14 - locale,
15 - name: 'flip',
16 - makeSvgIcon,
17 - menuBarPosition,
18 - templateHtml,
19 - usageStatistics,
20 - });
21 - this.flipStatus = false;
22 -
23 - this._els = {
24 - flipButton: this.selector('.tie-flip-button'),
25 - };
26 - }
27 -
28 - /**
29 - * Destroys the instance.
30 - */
31 - destroy() {
32 - this._removeEvent();
33 -
34 - assignmentForDestroy(this);
35 - }
36 -
37 - /**
38 - * Add event for flip
39 - * @param {Object} actions - actions for flip
40 - * @param {Function} actions.flip - flip action
41 - */
42 - addEvent(actions) {
43 - this.eventHandler.changeFlip = this._changeFlip.bind(this);
44 - this._actions = actions;
45 - this._els.flipButton.addEventListener('click', this.eventHandler.changeFlip);
46 - }
47 -
48 - /**
49 - * Remove event
50 - * @private
51 - */
52 - _removeEvent() {
53 - this._els.flipButton.removeEventListener('click', this.eventHandler.changeFlip);
54 - }
55 -
56 - /**
57 - * change Flip status
58 - * @param {object} event - change event
59 - * @private
60 - */
61 - _changeFlip(event) {
62 - const button = event.target.closest('.tui-image-editor-button');
63 - if (button) {
64 - const flipType = this.getButtonType(button, ['flipX', 'flipY', 'resetFlip']);
65 - if (!this.flipStatus && flipType === 'resetFlip') {
66 - return;
67 - }
68 -
69 - this._actions.flip(flipType).then((flipStatus) => {
70 - const flipClassList = this._els.flipButton.classList;
71 - this.flipStatus = false;
72 -
73 - flipClassList.remove('resetFlip');
74 - snippet.forEach(['flipX', 'flipY'], (type) => {
75 - flipClassList.remove(type);
76 - if (flipStatus[type]) {
77 - flipClassList.add(type);
78 - flipClassList.add('resetFlip');
79 - this.flipStatus = true;
80 - }
81 - });
82 - });
83 - }
84 - }
85 -}
86 -
87 -export default Flip;
1 -import snippet from 'tui-code-snippet';
2 -import Colorpicker from './tools/colorpicker';
3 -import Submenu from './submenuBase';
4 -import templateHtml from './template/submenu/icon';
5 -import { isSupportFileApi, assignmentForDestroy } from '../util';
6 -import { defaultIconPath } from '../consts';
7 -
8 -/**
9 - * Icon ui class
10 - * @class
11 - * @ignore
12 - */
13 -class Icon extends Submenu {
14 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
15 - super(subMenuElement, {
16 - locale,
17 - name: 'icon',
18 - makeSvgIcon,
19 - menuBarPosition,
20 - templateHtml,
21 - usageStatistics,
22 - });
23 -
24 - this.iconType = null;
25 - this._iconMap = {};
26 -
27 - this._els = {
28 - registrIconButton: this.selector('.tie-icon-image-file'),
29 - addIconButton: this.selector('.tie-icon-add-button'),
30 - iconColorpicker: new Colorpicker(
31 - this.selector('.tie-icon-color'),
32 - '#ffbb3b',
33 - this.toggleDirection,
34 - this.usageStatistics
35 - ),
36 - };
37 - }
38 -
39 - /**
40 - * Destroys the instance.
41 - */
42 - destroy() {
43 - this._removeEvent();
44 - this._els.iconColorpicker.destroy();
45 -
46 - assignmentForDestroy(this);
47 - }
48 -
49 - /**
50 - * Add event for icon
51 - * @param {Object} actions - actions for icon
52 - * @param {Function} actions.registCustomIcon - register icon
53 - * @param {Function} actions.addIcon - add icon
54 - * @param {Function} actions.changeColor - change icon color
55 - */
56 - addEvent(actions) {
57 - const registerIcon = this._registerIconHandler.bind(this);
58 - const addIcon = this._addIconHandler.bind(this);
59 -
60 - this.eventHandler = {
61 - registerIcon,
62 - addIcon,
63 - };
64 -
65 - this.actions = actions;
66 - this._els.iconColorpicker.on('change', this._changeColorHandler.bind(this));
67 - this._els.registrIconButton.addEventListener('change', registerIcon);
68 - this._els.addIconButton.addEventListener('click', addIcon);
69 - }
70 -
71 - /**
72 - * Remove event
73 - * @private
74 - */
75 - _removeEvent() {
76 - this._els.iconColorpicker.off();
77 - this._els.registrIconButton.removeEventListener('change', this.eventHandler.registerIcon);
78 - this._els.addIconButton.removeEventListener('click', this.eventHandler.addIcon);
79 - }
80 -
81 - /**
82 - * Clear icon type
83 - */
84 - clearIconType() {
85 - this._els.addIconButton.classList.remove(this.iconType);
86 - this.iconType = null;
87 - }
88 -
89 - /**
90 - * Register default icon
91 - */
92 - registDefaultIcon() {
93 - snippet.forEach(defaultIconPath, (path, type) => {
94 - this.actions.registDefalutIcons(type, path);
95 - });
96 - }
97 -
98 - /**
99 - * Set icon picker color
100 - * @param {string} iconColor - rgb color string
101 - */
102 - setIconPickerColor(iconColor) {
103 - this._els.iconColorpicker.color = iconColor;
104 - }
105 -
106 - /**
107 - * Returns the menu to its default state.
108 - */
109 - changeStandbyMode() {
110 - this.clearIconType();
111 - this.actions.cancelAddIcon();
112 - }
113 -
114 - /**
115 - * Change icon color
116 - * @param {string} color - color for change
117 - * @private
118 - */
119 - _changeColorHandler(color) {
120 - color = color || 'transparent';
121 - this.actions.changeColor(color);
122 - }
123 -
124 - /**
125 - * Change icon color
126 - * @param {object} event - add button event object
127 - * @private
128 - */
129 - _addIconHandler(event) {
130 - const button = event.target.closest('.tui-image-editor-button');
131 -
132 - if (button) {
133 - const iconType = button.getAttribute('data-icontype');
134 - const iconColor = this._els.iconColorpicker.color;
135 - this.actions.discardSelection();
136 - this.actions.changeSelectableAll(false);
137 - this._els.addIconButton.classList.remove(this.iconType);
138 - this._els.addIconButton.classList.add(iconType);
139 -
140 - if (this.iconType === iconType) {
141 - this.changeStandbyMode();
142 - } else {
143 - this.actions.addIcon(iconType, iconColor);
144 - this.iconType = iconType;
145 - }
146 - }
147 - }
148 -
149 - /**
150 - * register icon
151 - * @param {object} event - file change event object
152 - * @private
153 - */
154 - _registerIconHandler(event) {
155 - let imgUrl;
156 -
157 - if (!isSupportFileApi) {
158 - alert('This browser does not support file-api');
159 - }
160 -
161 - const [file] = event.target.files;
162 -
163 - if (file) {
164 - imgUrl = URL.createObjectURL(file);
165 - this.actions.registCustomIcon(imgUrl, file);
166 - }
167 - }
168 -}
169 -
170 -export default Icon;
1 -/**
2 - * Translate messages
3 - */
4 -class Locale {
5 - constructor(locale) {
6 - this._locale = locale;
7 - }
8 -
9 - /**
10 - * localize message
11 - * @param {string} message - message who will be localized
12 - * @returns {string}
13 - */
14 - localize(message) {
15 - return this._locale[message] || message;
16 - }
17 -}
18 -
19 -export default Locale;
1 -import Submenu from './submenuBase';
2 -import { assignmentForDestroy, isSupportFileApi } from '../util';
3 -import templateHtml from './template/submenu/mask';
4 -
5 -/**
6 - * Mask ui class
7 - * @class
8 - * @ignore
9 - */
10 -class Mask extends Submenu {
11 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
12 - super(subMenuElement, {
13 - locale,
14 - name: 'mask',
15 - makeSvgIcon,
16 - menuBarPosition,
17 - templateHtml,
18 - usageStatistics,
19 - });
20 -
21 - this._els = {
22 - applyButton: this.selector('.tie-mask-apply'),
23 - maskImageButton: this.selector('.tie-mask-image-file'),
24 - };
25 - }
26 -
27 - /**
28 - * Destroys the instance.
29 - */
30 - destroy() {
31 - this._removeEvent();
32 -
33 - assignmentForDestroy(this);
34 - }
35 -
36 - /**
37 - * Add event for mask
38 - * @param {Object} actions - actions for crop
39 - * @param {Function} actions.loadImageFromURL - load image action
40 - * @param {Function} actions.applyFilter - apply filter action
41 - */
42 - addEvent(actions) {
43 - const loadMaskFile = this._loadMaskFile.bind(this);
44 - const applyMask = this._applyMask.bind(this);
45 -
46 - this.eventHandler = {
47 - loadMaskFile,
48 - applyMask,
49 - };
50 -
51 - this.actions = actions;
52 - this._els.maskImageButton.addEventListener('change', loadMaskFile);
53 - this._els.applyButton.addEventListener('click', applyMask);
54 - }
55 -
56 - /**
57 - * Remove event
58 - * @private
59 - */
60 - _removeEvent() {
61 - this._els.maskImageButton.removeEventListener('change', this.eventHandler.loadMaskFile);
62 - this._els.applyButton.removeEventListener('click', this.eventHandler.applyMask);
63 - }
64 -
65 - /**
66 - * Apply mask
67 - * @private
68 - */
69 - _applyMask() {
70 - this.actions.applyFilter();
71 - this._els.applyButton.classList.remove('active');
72 - }
73 -
74 - /**
75 - * Load mask file
76 - * @param {object} event - File change event object
77 - * @private
78 - */
79 - _loadMaskFile(event) {
80 - let imgUrl;
81 -
82 - if (!isSupportFileApi()) {
83 - alert('This browser does not support file-api');
84 - }
85 -
86 - const [file] = event.target.files;
87 -
88 - if (file) {
89 - imgUrl = URL.createObjectURL(file);
90 - this.actions.loadImageFromURL(imgUrl, file);
91 - this._els.applyButton.classList.add('active');
92 - }
93 - }
94 -}
95 -
96 -export default Mask;
1 -import Range from './tools/range';
2 -import Submenu from './submenuBase';
3 -import templateHtml from './template/submenu/rotate';
4 -import { toInteger, assignmentForDestroy } from '../util';
5 -import { defaultRotateRangeValus } from '../consts';
6 -
7 -const CLOCKWISE = 30;
8 -const COUNTERCLOCKWISE = -30;
9 -
10 -/**
11 - * Rotate ui class
12 - * @class
13 - * @ignore
14 - */
15 -class Rotate extends Submenu {
16 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
17 - super(subMenuElement, {
18 - locale,
19 - name: 'rotate',
20 - makeSvgIcon,
21 - menuBarPosition,
22 - templateHtml,
23 - usageStatistics,
24 - });
25 - this._value = 0;
26 -
27 - this._els = {
28 - rotateButton: this.selector('.tie-retate-button'),
29 - rotateRange: new Range(
30 - {
31 - slider: this.selector('.tie-rotate-range'),
32 - input: this.selector('.tie-ratate-range-value'),
33 - },
34 - defaultRotateRangeValus
35 - ),
36 - };
37 - }
38 -
39 - /**
40 - * Destroys the instance.
41 - */
42 - destroy() {
43 - this._removeEvent();
44 - this._els.rotateRange.destroy();
45 -
46 - assignmentForDestroy(this);
47 - }
48 -
49 - setRangeBarAngle(type, angle) {
50 - let resultAngle = angle;
51 -
52 - if (type === 'rotate') {
53 - resultAngle = parseInt(this._els.rotateRange.value, 10) + angle;
54 - }
55 -
56 - this._setRangeBarRatio(resultAngle);
57 - }
58 -
59 - _setRangeBarRatio(angle) {
60 - this._els.rotateRange.value = angle;
61 - }
62 -
63 - /**
64 - * Add event for rotate
65 - * @param {Object} actions - actions for crop
66 - * @param {Function} actions.rotate - rotate action
67 - * @param {Function} actions.setAngle - set angle action
68 - */
69 - addEvent(actions) {
70 - this.eventHandler.rotationAngleChanged = this._changeRotateForButton.bind(this);
71 -
72 - // {rotate, setAngle}
73 - this.actions = actions;
74 - this._els.rotateButton.addEventListener('click', this.eventHandler.rotationAngleChanged);
75 - this._els.rotateRange.on('change', this._changeRotateForRange.bind(this));
76 - }
77 -
78 - /**
79 - * Remove event
80 - * @private
81 - */
82 - _removeEvent() {
83 - this._els.rotateButton.removeEventListener('click', this.eventHandler.rotationAngleChanged);
84 - this._els.rotateRange.off();
85 - }
86 -
87 - /**
88 - * Change rotate for range
89 - * @param {number} value - angle value
90 - * @param {boolean} isLast - Is last change
91 - * @private
92 - */
93 - _changeRotateForRange(value, isLast) {
94 - const angle = toInteger(value);
95 - this.actions.setAngle(angle, !isLast);
96 - this._value = angle;
97 - }
98 -
99 - /**
100 - * Change rotate for button
101 - * @param {object} event - add button event object
102 - * @private
103 - */
104 - _changeRotateForButton(event) {
105 - const button = event.target.closest('.tui-image-editor-button');
106 - const angle = this._els.rotateRange.value;
107 -
108 - if (button) {
109 - const rotateType = this.getButtonType(button, ['counterclockwise', 'clockwise']);
110 - const rotateAngle = {
111 - clockwise: CLOCKWISE,
112 - counterclockwise: COUNTERCLOCKWISE,
113 - }[rotateType];
114 - const newAngle = parseInt(angle, 10) + rotateAngle;
115 - const isRotatable = newAngle >= -360 && newAngle <= 360;
116 - if (isRotatable) {
117 - this.actions.rotate(rotateAngle);
118 - }
119 - }
120 - }
121 -}
122 -
123 -export default Rotate;
1 -import Colorpicker from './tools/colorpicker';
2 -import Range from './tools/range';
3 -import Submenu from './submenuBase';
4 -import templateHtml from './template/submenu/shape';
5 -import { toInteger, assignmentForDestroy } from '../util';
6 -import { defaultShapeStrokeValus } from '../consts';
7 -
8 -const SHAPE_DEFAULT_OPTION = {
9 - stroke: '#ffbb3b',
10 - fill: '',
11 - strokeWidth: 3,
12 -};
13 -
14 -/**
15 - * Shape ui class
16 - * @class
17 - * @ignore
18 - */
19 -class Shape extends Submenu {
20 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
21 - super(subMenuElement, {
22 - locale,
23 - name: 'shape',
24 - makeSvgIcon,
25 - menuBarPosition,
26 - templateHtml,
27 - usageStatistics,
28 - });
29 - this.type = null;
30 - this.options = SHAPE_DEFAULT_OPTION;
31 -
32 - this._els = {
33 - shapeSelectButton: this.selector('.tie-shape-button'),
34 - shapeColorButton: this.selector('.tie-shape-color-button'),
35 - strokeRange: new Range(
36 - {
37 - slider: this.selector('.tie-stroke-range'),
38 - input: this.selector('.tie-stroke-range-value'),
39 - },
40 - defaultShapeStrokeValus
41 - ),
42 - fillColorpicker: new Colorpicker(
43 - this.selector('.tie-color-fill'),
44 - '',
45 - this.toggleDirection,
46 - this.usageStatistics
47 - ),
48 - strokeColorpicker: new Colorpicker(
49 - this.selector('.tie-color-stroke'),
50 - '#ffbb3b',
51 - this.toggleDirection,
52 - this.usageStatistics
53 - ),
54 - };
55 -
56 - this.colorPickerControls.push(this._els.fillColorpicker);
57 - this.colorPickerControls.push(this._els.strokeColorpicker);
58 - }
59 -
60 - /**
61 - * Destroys the instance.
62 - */
63 - destroy() {
64 - this._removeEvent();
65 - this._els.strokeRange.destroy();
66 - this._els.fillColorpicker.destroy();
67 - this._els.strokeColorpicker.destroy();
68 -
69 - assignmentForDestroy(this);
70 - }
71 -
72 - /**
73 - * Add event for shape
74 - * @param {Object} actions - actions for shape
75 - * @param {Function} actions.changeShape - change shape mode
76 - * @param {Function} actions.setDrawingShape - set dreawing shape
77 - */
78 - addEvent(actions) {
79 - this.eventHandler.shapeTypeSelected = this._changeShapeHandler.bind(this);
80 - this.actions = actions;
81 -
82 - this._els.shapeSelectButton.addEventListener('click', this.eventHandler.shapeTypeSelected);
83 - this._els.strokeRange.on('change', this._changeStrokeRangeHandler.bind(this));
84 - this._els.fillColorpicker.on('change', this._changeFillColorHandler.bind(this));
85 - this._els.strokeColorpicker.on('change', this._changeStrokeColorHandler.bind(this));
86 - this._els.fillColorpicker.on('changeShow', this.colorPickerChangeShow.bind(this));
87 - this._els.strokeColorpicker.on('changeShow', this.colorPickerChangeShow.bind(this));
88 - }
89 -
90 - /**
91 - * Remove event
92 - * @private
93 - */
94 - _removeEvent() {
95 - this._els.shapeSelectButton.removeEventListener('click', this.eventHandler.shapeTypeSelected);
96 - this._els.strokeRange.off();
97 - this._els.fillColorpicker.off();
98 - this._els.strokeColorpicker.off();
99 - }
100 -
101 - /**
102 - * Set Shape status
103 - * @param {Object} options - options of shape status
104 - * @param {string} strokeWidth - stroke width
105 - * @param {string} strokeColor - stroke color
106 - * @param {string} fillColor - fill color
107 - */
108 - setShapeStatus({ strokeWidth, strokeColor, fillColor }) {
109 - this._els.strokeRange.value = strokeWidth;
110 - this._els.strokeColorpicker.color = strokeColor;
111 - this._els.fillColorpicker.color = fillColor;
112 - this.options.stroke = strokeColor;
113 - this.options.fill = fillColor;
114 - this.options.strokeWidth = strokeWidth;
115 -
116 - this.actions.setDrawingShape(this.type, { strokeWidth });
117 - }
118 -
119 - /**
120 - * Executed when the menu starts.
121 - */
122 - changeStartMode() {
123 - this.actions.stopDrawingMode();
124 - }
125 -
126 - /**
127 - * Returns the menu to its default state.
128 - */
129 - changeStandbyMode() {
130 - this.type = null;
131 - this.actions.changeSelectableAll(true);
132 - this._els.shapeSelectButton.classList.remove('circle');
133 - this._els.shapeSelectButton.classList.remove('triangle');
134 - this._els.shapeSelectButton.classList.remove('rect');
135 - }
136 -
137 - /**
138 - * set range stroke max value
139 - * @param {number} maxValue - expect max value for change
140 - */
141 - setMaxStrokeValue(maxValue) {
142 - let strokeMaxValue = maxValue;
143 - if (strokeMaxValue <= 0) {
144 - strokeMaxValue = defaultShapeStrokeValus.max;
145 - }
146 - this._els.strokeRange.max = strokeMaxValue;
147 - }
148 -
149 - /**
150 - * Set stroke value
151 - * @param {number} value - expect value for strokeRange change
152 - */
153 - setStrokeValue(value) {
154 - this._els.strokeRange.value = value;
155 - this._els.strokeRange.trigger('change');
156 - }
157 -
158 - /**
159 - * Get stroke value
160 - * @returns {number} - stroke range value
161 - */
162 - getStrokeValue() {
163 - return this._els.strokeRange.value;
164 - }
165 -
166 - /**
167 - * Change icon color
168 - * @param {object} event - add button event object
169 - * @private
170 - */
171 - _changeShapeHandler(event) {
172 - const button = event.target.closest('.tui-image-editor-button');
173 - if (button) {
174 - this.actions.stopDrawingMode();
175 - this.actions.discardSelection();
176 - const shapeType = this.getButtonType(button, ['circle', 'triangle', 'rect']);
177 -
178 - if (this.type === shapeType) {
179 - this.changeStandbyMode();
180 -
181 - return;
182 - }
183 - this.changeStandbyMode();
184 - this.type = shapeType;
185 - event.currentTarget.classList.add(shapeType);
186 - this.actions.changeSelectableAll(false);
187 - this.actions.modeChange('shape');
188 - }
189 - }
190 -
191 - /**
192 - * Change stroke range
193 - * @param {number} value - stroke range value
194 - * @param {boolean} isLast - Is last change
195 - * @private
196 - */
197 - _changeStrokeRangeHandler(value, isLast) {
198 - this.options.strokeWidth = toInteger(value);
199 - this.actions.changeShape(
200 - {
201 - strokeWidth: value,
202 - },
203 - !isLast
204 - );
205 -
206 - this.actions.setDrawingShape(this.type, this.options);
207 - }
208 -
209 - /**
210 - * Change shape color
211 - * @param {string} color - fill color
212 - * @private
213 - */
214 - _changeFillColorHandler(color) {
215 - color = color || 'transparent';
216 - this.options.fill = color;
217 - this.actions.changeShape({
218 - fill: color,
219 - });
220 - }
221 -
222 - /**
223 - * Change shape stroke color
224 - * @param {string} color - fill color
225 - * @private
226 - */
227 - _changeStrokeColorHandler(color) {
228 - color = color || 'transparent';
229 - this.options.stroke = color;
230 - this.actions.changeShape({
231 - stroke: color,
232 - });
233 - }
234 -}
235 -
236 -export default Shape;
1 -/**
2 - * Submenu Base Class
3 - * @class
4 - * @ignore
5 - */
6 -class Submenu {
7 - /**
8 - * @param {HTMLElement} subMenuElement - submenu dom element
9 - * @param {Locale} locale - translate text
10 - * @param {string} name - name of sub menu
11 - * @param {Object} iconStyle - style of icon
12 - * @param {string} menuBarPosition - position of menu
13 - * @param {*} templateHtml - template for SubMenuElement
14 - * @param {boolean} [usageStatistics=false] - template for SubMenuElement
15 - */
16 - constructor(
17 - subMenuElement,
18 - { locale, name, makeSvgIcon, menuBarPosition, templateHtml, usageStatistics }
19 - ) {
20 - this.subMenuElement = subMenuElement;
21 - this.menuBarPosition = menuBarPosition;
22 - this.toggleDirection = menuBarPosition === 'top' ? 'down' : 'up';
23 - this.colorPickerControls = [];
24 - this.usageStatistics = usageStatistics;
25 - this.eventHandler = {};
26 - this._makeSubMenuElement({
27 - locale,
28 - name,
29 - makeSvgIcon,
30 - templateHtml,
31 - });
32 - }
33 -
34 - /**
35 - * editor dom ui query selector
36 - * @param {string} selectName - query selector string name
37 - * @returns {HTMLElement}
38 - */
39 - selector(selectName) {
40 - return this.subMenuElement.querySelector(selectName);
41 - }
42 -
43 - /**
44 - * change show state change for colorpicker instance
45 - * @param {Colorpicker} occurredControl - target Colorpicker Instance
46 - */
47 - colorPickerChangeShow(occurredControl) {
48 - this.colorPickerControls.forEach((pickerControl) => {
49 - if (occurredControl !== pickerControl) {
50 - pickerControl.hide();
51 - }
52 - });
53 - }
54 -
55 - /**
56 - * Get butten type
57 - * @param {HTMLElement} button - event target element
58 - * @param {array} buttonNames - Array of button names
59 - * @returns {string} - button type
60 - */
61 - getButtonType(button, buttonNames) {
62 - return button.className.match(RegExp(`(${buttonNames.join('|')})`))[0];
63 - }
64 -
65 - /**
66 - * Get butten type
67 - * @param {HTMLElement} target - event target element
68 - * @param {string} removeClass - remove class name
69 - * @param {string} addClass - add class name
70 - */
71 - changeClass(target, removeClass, addClass) {
72 - target.classList.remove(removeClass);
73 - target.classList.add(addClass);
74 - }
75 -
76 - /**
77 - * Interface method whose implementation is optional.
78 - * Returns the menu to its default state.
79 - */
80 - changeStandbyMode() {}
81 -
82 - /**
83 - * Interface method whose implementation is optional.
84 - * Executed when the menu starts.
85 - */
86 - changeStartMode() {}
87 -
88 - /**
89 - * Make submenu dom element
90 - * @param {Locale} locale - translate text
91 - * @param {string} name - submenu name
92 - * @param {Object} iconStyle - icon style
93 - * @param {*} templateHtml - template for SubMenuElement
94 - * @private
95 - */
96 - _makeSubMenuElement({ locale, name, iconStyle, makeSvgIcon, templateHtml }) {
97 - const iconSubMenu = document.createElement('div');
98 - iconSubMenu.className = `tui-image-editor-menu-${name}`;
99 - iconSubMenu.innerHTML = templateHtml({
100 - locale,
101 - iconStyle,
102 - makeSvgIcon,
103 - });
104 -
105 - this.subMenuElement.appendChild(iconSubMenu);
106 - }
107 -}
108 -
109 -export default Submenu;
1 -export default ({ locale, biImage, loadButtonStyle, downloadButtonStyle }) => `
2 - <div class="tui-image-editor-controls">
3 - <div class="tui-image-editor-controls-logo">
4 - <img src="${biImage}" />
5 - </div>
6 - <ul class="tui-image-editor-menu"></ul>
7 -
8 - <div class="tui-image-editor-controls-buttons">
9 - <div style="${loadButtonStyle}">
10 - ${locale.localize('Load')}
11 - <input type="file" class="tui-image-editor-load-btn" />
12 - </div>
13 - <button class="tui-image-editor-download-btn" style="${downloadButtonStyle}">
14 - ${locale.localize('Download')}
15 - </button>
16 - </div>
17 - </div>
18 -`;
1 -export default ({
2 - locale,
3 - biImage,
4 - commonStyle,
5 - headerStyle,
6 - loadButtonStyle,
7 - downloadButtonStyle,
8 - submenuStyle,
9 -}) => `
10 - <div class="tui-image-editor-main-container" style="${commonStyle}">
11 - <div class="tui-image-editor-header" style="${headerStyle}">
12 - <div class="tui-image-editor-header-logo">
13 - <img src="${biImage}" />
14 - </div>
15 - <div class="tui-image-editor-header-buttons">
16 - <div style="${loadButtonStyle}">
17 - ${locale.localize('Load')}
18 - <input type="file" class="tui-image-editor-load-btn" />
19 - </div>
20 - <button class="tui-image-editor-download-btn" style="${downloadButtonStyle}">
21 - ${locale.localize('Download')}
22 - </button>
23 - </div>
24 - </div>
25 - <div class="tui-image-editor-main">
26 - <div class="tui-image-editor-submenu">
27 - <div class="tui-image-editor-submenu-style" style="${submenuStyle}"></div>
28 - </div>
29 - <div class="tui-image-editor-wrap">
30 - <div class="tui-image-editor-size-wrap">
31 - <div class="tui-image-editor-align-wrap">
32 - <div class="tui-image-editor"></div>
33 - </div>
34 - </div>
35 - </div>
36 - </div>
37 - </div>
38 -`;
1 -export default ({
2 - subMenuLabelActive,
3 - subMenuLabelNormal,
4 - subMenuRangeTitle,
5 - submenuPartitionVertical,
6 - submenuPartitionHorizontal,
7 - submenuCheckbox,
8 - submenuRangePointer,
9 - submenuRangeValue,
10 - submenuColorpickerTitle,
11 - submenuColorpickerButton,
12 - submenuRangeBar,
13 - submenuRangeSubbar,
14 - submenuDisabledRangePointer,
15 - submenuDisabledRangeBar,
16 - submenuDisabledRangeSubbar,
17 - submenuIconSize,
18 - menuIconSize,
19 - biSize,
20 - menuIconStyle,
21 - submenuIconStyle,
22 -}) => `
23 - .tie-icon-add-button.icon-bubble .tui-image-editor-button[data-icontype="icon-bubble"] label,
24 - .tie-icon-add-button.icon-heart .tui-image-editor-button[data-icontype="icon-heart"] label,
25 - .tie-icon-add-button.icon-location .tui-image-editor-button[data-icontype="icon-location"] label,
26 - .tie-icon-add-button.icon-polygon .tui-image-editor-button[data-icontype="icon-polygon"] label,
27 - .tie-icon-add-button.icon-star .tui-image-editor-button[data-icontype="icon-star"] label,
28 - .tie-icon-add-button.icon-star-2 .tui-image-editor-button[data-icontype="icon-star-2"] label,
29 - .tie-icon-add-button.icon-arrow-3 .tui-image-editor-button[data-icontype="icon-arrow-3"] label,
30 - .tie-icon-add-button.icon-arrow-2 .tui-image-editor-button[data-icontype="icon-arrow-2"] label,
31 - .tie-icon-add-button.icon-arrow .tui-image-editor-button[data-icontype="icon-arrow"] label,
32 - .tie-icon-add-button.icon-bubble .tui-image-editor-button[data-icontype="icon-bubble"] label,
33 - .tie-draw-line-select-button.line .tui-image-editor-button.line label,
34 - .tie-draw-line-select-button.free .tui-image-editor-button.free label,
35 - .tie-flip-button.flipX .tui-image-editor-button.flipX label,
36 - .tie-flip-button.flipY .tui-image-editor-button.flipY label,
37 - .tie-flip-button.resetFlip .tui-image-editor-button.resetFlip label,
38 - .tie-crop-button .tui-image-editor-button.apply.active label,
39 - .tie-crop-preset-button .tui-image-editor-button.preset.active label,
40 - .tie-shape-button.rect .tui-image-editor-button.rect label,
41 - .tie-shape-button.circle .tui-image-editor-button.circle label,
42 - .tie-shape-button.triangle .tui-image-editor-button.triangle label,
43 - .tie-text-effect-button .tui-image-editor-button.active label,
44 - .tie-text-align-button.left .tui-image-editor-button.left label,
45 - .tie-text-align-button.center .tui-image-editor-button.center label,
46 - .tie-text-align-button.right .tui-image-editor-button.right label,
47 - .tie-mask-apply.apply.active .tui-image-editor-button.apply label,
48 - .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button:hover > label,
49 - .tui-image-editor-container .tui-image-editor-checkbox label > span {
50 - ${subMenuLabelActive}
51 - }
52 - .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button > label,
53 - .tui-image-editor-container .tui-image-editor-range-wrap.tui-image-editor-newline.short label,
54 - .tui-image-editor-container .tui-image-editor-range-wrap.tui-image-editor-newline.short label > span {
55 - ${subMenuLabelNormal}
56 - }
57 - .tui-image-editor-container .tui-image-editor-range-wrap label > span {
58 - ${subMenuRangeTitle}
59 - }
60 - .tui-image-editor-container .tui-image-editor-partition > div {
61 - ${submenuPartitionVertical}
62 - }
63 - .tui-image-editor-container.left .tui-image-editor-submenu .tui-image-editor-partition > div,
64 - .tui-image-editor-container.right .tui-image-editor-submenu .tui-image-editor-partition > div {
65 - ${submenuPartitionHorizontal}
66 - }
67 - .tui-image-editor-container .tui-image-editor-checkbox label > span:before {
68 - ${submenuCheckbox}
69 - }
70 - .tui-image-editor-container .tui-image-editor-checkbox label > input:checked + span:before {
71 - border: 0;
72 - }
73 - .tui-image-editor-container .tui-image-editor-virtual-range-pointer {
74 - ${submenuRangePointer}
75 - }
76 - .tui-image-editor-container .tui-image-editor-virtual-range-bar {
77 - ${submenuRangeBar}
78 - }
79 - .tui-image-editor-container .tui-image-editor-virtual-range-subbar {
80 - ${submenuRangeSubbar}
81 - }
82 - .tui-image-editor-container .tui-image-editor-disabled .tui-image-editor-virtual-range-pointer {
83 - ${submenuDisabledRangePointer}
84 - }
85 - .tui-image-editor-container .tui-image-editor-disabled .tui-image-editor-virtual-range-subbar {
86 - ${submenuDisabledRangeSubbar}
87 - }
88 - .tui-image-editor-container .tui-image-editor-disabled .tui-image-editor-virtual-range-bar {
89 - ${submenuDisabledRangeBar}
90 - }
91 - .tui-image-editor-container .tui-image-editor-range-value {
92 - ${submenuRangeValue}
93 - }
94 - .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button .color-picker-value + label {
95 - ${submenuColorpickerTitle}
96 - }
97 - .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button .color-picker-value {
98 - ${submenuColorpickerButton}
99 - }
100 - .tui-image-editor-container .svg_ic-menu {
101 - ${menuIconSize}
102 - }
103 - .tui-image-editor-container .svg_ic-submenu {
104 - ${submenuIconSize}
105 - }
106 - .tui-image-editor-container .tui-image-editor-controls-logo > img,
107 - .tui-image-editor-container .tui-image-editor-header-logo > img {
108 - ${biSize}
109 - }
110 - .tui-image-editor-menu use.normal.use-default {
111 - fill-rule: evenodd;
112 - fill: ${menuIconStyle.normal.color};
113 - stroke: ${menuIconStyle.normal.color};
114 - }
115 - .tui-image-editor-menu use.active.use-default {
116 - fill-rule: evenodd;
117 - fill: ${menuIconStyle.active.color};
118 - stroke: ${menuIconStyle.active.color};
119 - }
120 - .tui-image-editor-menu use.hover.use-default {
121 - fill-rule: evenodd;
122 - fill: ${menuIconStyle.hover.color};
123 - stroke: ${menuIconStyle.hover.color};
124 - }
125 - .tui-image-editor-menu use.disabled.use-default {
126 - fill-rule: evenodd;
127 - fill: ${menuIconStyle.disabled.color};
128 - stroke: ${menuIconStyle.disabled.color};
129 - }
130 - .tui-image-editor-submenu use.normal.use-default {
131 - fill-rule: evenodd;
132 - fill: ${submenuIconStyle.normal.color};
133 - stroke: ${submenuIconStyle.normal.color};
134 - }
135 - .tui-image-editor-submenu use.active.use-default {
136 - fill-rule: evenodd;
137 - fill: ${submenuIconStyle.active.color};
138 - stroke: ${submenuIconStyle.active.color};
139 - }
140 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-crop-preset-button">
10 - <div class="tui-image-editor-button preset preset-none active">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'shape-rectangle', true)}
13 - </div>
14 - <label> ${locale.localize('Custom')} </label>
15 - </div>
16 - <div class="tui-image-editor-button preset preset-square">
17 - <div>
18 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
19 - </div>
20 - <label> ${locale.localize('Square')} </label>
21 - </div>
22 - <div class="tui-image-editor-button preset preset-3-2">
23 - <div>
24 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
25 - </div>
26 - <label> ${locale.localize('3:2')} </label>
27 - </div>
28 - <div class="tui-image-editor-button preset preset-4-3">
29 - <div>
30 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
31 - </div>
32 - <label> ${locale.localize('4:3')} </label>
33 - </div>
34 - <div class="tui-image-editor-button preset preset-5-4">
35 - <div>
36 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
37 - </div>
38 - <label> ${locale.localize('5:4')} </label>
39 - </div>
40 - <div class="tui-image-editor-button preset preset-7-5">
41 - <div>
42 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
43 - </div>
44 - <label> ${locale.localize('7:5')} </label>
45 - </div>
46 - <div class="tui-image-editor-button preset preset-16-9">
47 - <div>
48 - ${makeSvgIcon(['normal', 'active'], 'crop', true)}
49 - </div>
50 - <label> ${locale.localize('16:9')} </label>
51 - </div>
52 - </li>
53 - <li class="tui-image-editor-partition tui-image-editor-newline">
54 - </li>
55 - <li class="tui-image-editor-partition only-left-right">
56 - <div></div>
57 - </li>
58 - <li class="tie-crop-button action">
59 - <div class="tui-image-editor-button apply">
60 - ${makeSvgIcon(['normal', 'active'], 'apply')}
61 - <label>
62 - ${locale.localize('Apply')}
63 - </label>
64 - </div>
65 - <div class="tui-image-editor-button cancel">
66 - ${makeSvgIcon(['normal', 'active'], 'cancel')}
67 - <label>
68 - ${locale.localize('Cancel')}
69 - </label>
70 - </div>
71 - </li>
72 - </ul>
73 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-draw-line-select-button">
10 - <div class="tui-image-editor-button free">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'draw-free', true)}
13 - </div>
14 - <label>
15 - ${locale.localize('Free')}
16 - </label>
17 - </div>
18 - <div class="tui-image-editor-button line">
19 - <div>
20 - ${makeSvgIcon(['normal', 'active'], 'draw-line', true)}
21 - </div>
22 - <label>
23 - ${locale.localize('Straight')}
24 - </label>
25 - </div>
26 - </li>
27 - <li class="tui-image-editor-partition">
28 - <div></div>
29 - </li>
30 - <li>
31 - <div class="tie-draw-color" title="${locale.localize('Color')}"></div>
32 - </li>
33 - <li class="tui-image-editor-partition only-left-right">
34 - <div></div>
35 - </li>
36 - <li class="tui-image-editor-newline tui-image-editor-range-wrap">
37 - <label class="range">${locale.localize('Range')}</label>
38 - <div class="tie-draw-range"></div>
39 - <input class="tie-draw-range-value tui-image-editor-range-value" value="0" />
40 - </li>
41 - </ul>
42 -`;
1 -/**
2 - * @param {Locale} locale - Translate text
3 - * @returns {string}
4 - */
5 -export default ({ locale }) => `
6 - <ul class="tui-image-editor-submenu-item">
7 - <li class="tui-image-editor-submenu-align">
8 - <div class="tui-image-editor-checkbox-wrap fixed-width">
9 - <div class="tui-image-editor-checkbox">
10 - <label>
11 - <input type="checkbox" class="tie-grayscale">
12 - <span>${locale.localize('Grayscale')}</span>
13 - </label>
14 - </div>
15 - <div class="tui-image-editor-checkbox">
16 - <label>
17 - <input type="checkbox" class="tie-invert">
18 - <span>${locale.localize('Invert')}</span>
19 - </label>
20 - </div>
21 - <div class="tui-image-editor-checkbox">
22 - <label>
23 - <input type="checkbox" class="tie-sepia">
24 - <span>${locale.localize('Sepia')}</span>
25 - </label>
26 - </div>
27 - <div class="tui-image-editor-checkbox">
28 - <label>
29 - <input type="checkbox" class="tie-vintage">
30 - <span>${locale.localize('Sepia2')}</span>
31 - </label>
32 - </div>
33 - <div class="tui-image-editor-checkbox">
34 - <label>
35 - <input type="checkbox" class="tie-blur">
36 - <span>${locale.localize('Blur')}</span>
37 - </label>
38 - </div>
39 - <div class="tui-image-editor-checkbox">
40 - <label>
41 - <input type="checkbox" class="tie-sharpen">
42 - <span>${locale.localize('Sharpen')}</span>
43 - </label>
44 - </div>
45 - <div class="tui-image-editor-checkbox">
46 - <label>
47 - <input type="checkbox" class="tie-emboss">
48 - <span>${locale.localize('Emboss')}</span>
49 - </label>
50 - </div>
51 - </div>
52 - </li>
53 - <li class="tui-image-editor-partition">
54 - <div></div>
55 - </li>
56 - <li class="tui-image-editor-submenu-align">
57 - <div class="tui-image-editor-checkbox-group tui-image-editor-disabled" style="margin-bottom: 7px;">
58 - <div class="tui-image-editor-checkbox-wrap">
59 - <div class="tui-image-editor-checkbox">
60 - <label>
61 - <input type="checkbox" class="tie-remove-white">
62 - <span>${locale.localize('Remove White')}</span>
63 - </label>
64 - </div>
65 - </div>
66 - <div class="tui-image-editor-newline tui-image-editor-range-wrap short">
67 - <label>${locale.localize('Distance')}</label>
68 - <div class="tie-removewhite-distance-range"></div>
69 - </div>
70 - </div>
71 - <div class="tui-image-editor-checkbox-group tui-image-editor-disabled">
72 - <div class="tui-image-editor-checkbox">
73 - <label>
74 - <input type="checkbox" class="tie-brightness">
75 - <span>${locale.localize('Brightness')}</span>
76 - </label>
77 - </div>
78 - <div class="tui-image-editor-range-wrap short">
79 - <div class="tie-brightness-range"></div>
80 - </div>
81 - </div>
82 - <div class="tui-image-editor-checkbox-group tui-image-editor-disabled">
83 - <div class="tui-image-editor-checkbox">
84 - <label>
85 - <input type="checkbox" class="tie-noise">
86 - <span>${locale.localize('Noise')}</span>
87 - </label>
88 - </div>
89 - <div class="tui-image-editor-range-wrap short">
90 - <div class="tie-noise-range"></div>
91 - </div>
92 - </div>
93 - </li>
94 - <li class="tui-image-editor-partition only-left-right">
95 - <div></div>
96 - </li>
97 - <li class="tui-image-editor-submenu-align">
98 - <div class="tui-image-editor-checkbox-group tui-image-editor-disabled">
99 - <div class="tui-image-editor-checkbox">
100 - <label>
101 - <input type="checkbox" class="tie-pixelate">
102 - <span>${locale.localize('Pixelate')}</span>
103 - </label>
104 - </div>
105 - <div class="tui-image-editor-range-wrap short">
106 - <div class="tie-pixelate-range"></div>
107 - </div>
108 - </div>
109 - <div class="tui-image-editor-checkbox-group tui-image-editor-disabled">
110 - <div class="tui-image-editor-newline tui-image-editor-checkbox-wrap">
111 - <div class="tui-image-editor-checkbox">
112 - <label>
113 - <input type="checkbox" class="tie-color-filter">
114 - <span>${locale.localize('Color Filter')}</span>
115 - </label>
116 - </div>
117 - </div>
118 - <div class="tui-image-editor-newline tui-image-editor-range-wrap short">
119 - <label>${locale.localize('Threshold')}</label>
120 - <div class="tie-colorfilter-threshole-range"></div>
121 - </div>
122 - </div>
123 - </li>
124 - <li class="tui-image-editor-partition">
125 - <div></div>
126 - </li>
127 - <li>
128 - <div class="filter-color-item">
129 - <div class="tie-filter-tint-color" title="${locale.localize('Tint')}"></div>
130 - <div class="tui-image-editor-checkbox">
131 - <label>
132 - <input type="checkbox" class="tie-tint">
133 - <span></span>
134 - </label>
135 - </div>
136 - </div>
137 - <div class="filter-color-item">
138 - <div class="tie-filter-multiply-color" title="${locale.localize('Multiply')}"></div>
139 - <div class="tui-image-editor-checkbox">
140 - <label>
141 - <input type="checkbox" class="tie-multiply">
142 - <span></span>
143 - </label>
144 - </div>
145 - </div>
146 - <div class="filter-color-item">
147 - <div class="tie-filter-blend-color" title="${locale.localize('Blend')}"></div>
148 - <div class="tui-image-editor-checkbox">
149 - <label>
150 - <input type="checkbox" class="tie-blend">
151 - <span></span>
152 - </label>
153 - </div>
154 - </div>
155 - </li>
156 - </ul>
157 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tie-flip-button tui-image-editor-submenu-item">
9 - <li>
10 - <div class="tui-image-editor-button flipX">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'flip-x', true)}
13 - </div>
14 - <label>
15 - ${locale.localize('Flip X')}
16 - </label>
17 - </div>
18 - <div class="tui-image-editor-button flipY">
19 - <div>
20 - ${makeSvgIcon(['normal', 'active'], 'flip-y', true)}
21 - </div>
22 - <label>
23 - ${locale.localize('Flip Y')}
24 - </label>
25 - </div>
26 - </li>
27 - <li class="tui-image-editor-partition">
28 - <div></div>
29 - </li>
30 - <li>
31 - <div class="tui-image-editor-button resetFlip">
32 - <div>
33 - ${makeSvgIcon(['normal', 'active'], 'flip-reset', true)}
34 - </div>
35 - <label>
36 - ${locale.localize('Reset')}
37 - </label>
38 - </div>
39 - </li>
40 - </ul>
41 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-icon-add-button">
10 - <div class="tui-image-editor-button" data-icontype="icon-arrow">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'icon-arrow', true)}
13 - </div>
14 - <label>
15 - ${locale.localize('Arrow')}
16 - </label>
17 - </div>
18 - <div class="tui-image-editor-button" data-icontype="icon-arrow-2">
19 - <div>
20 - ${makeSvgIcon(['normal', 'active'], 'icon-arrow-2', true)}
21 - </div>
22 - <label>
23 - ${locale.localize('Arrow-2')}
24 - </label>
25 - </div>
26 - <div class="tui-image-editor-button" data-icontype="icon-arrow-3">
27 - <div>
28 - ${makeSvgIcon(['normal', 'active'], 'icon-arrow-3', true)}
29 - </div>
30 - <label>
31 - ${locale.localize('Arrow-3')}
32 - </label>
33 - </div>
34 - <div class="tui-image-editor-button" data-icontype="icon-star">
35 - <div>
36 - ${makeSvgIcon(['normal', 'active'], 'icon-star', true)}
37 - </div>
38 - <label>
39 - ${locale.localize('Star-1')}
40 - </label>
41 - </div>
42 - <div class="tui-image-editor-button" data-icontype="icon-star-2">
43 - <div>
44 - ${makeSvgIcon(['normal', 'active'], 'icon-star-2', true)}
45 - </div>
46 - <label>
47 - ${locale.localize('Star-2')}
48 - </label>
49 - </div>
50 -
51 - <div class="tui-image-editor-button" data-icontype="icon-polygon">
52 - <div>
53 - ${makeSvgIcon(['normal', 'active'], 'icon-polygon', true)}
54 - </div>
55 - <label>
56 - ${locale.localize('Polygon')}
57 - </label>
58 - </div>
59 -
60 - <div class="tui-image-editor-button" data-icontype="icon-location">
61 - <div>
62 - ${makeSvgIcon(['normal', 'active'], 'icon-location', true)}
63 - </div>
64 - <label>
65 - ${locale.localize('Location')}
66 - </label>
67 - </div>
68 -
69 - <div class="tui-image-editor-button" data-icontype="icon-heart">
70 - <div>
71 - ${makeSvgIcon(['normal', 'active'], 'icon-heart', true)}
72 - </div>
73 - <label>
74 - ${locale.localize('Heart')}
75 - </label>
76 - </div>
77 -
78 - <div class="tui-image-editor-button" data-icontype="icon-bubble">
79 - <div>
80 - ${makeSvgIcon(['normal', 'active'], 'icon-bubble', true)}
81 - </div>
82 - <label>
83 - ${locale.localize('Bubble')}
84 - </label>
85 - </div>
86 - </li>
87 - <li class="tui-image-editor-partition">
88 - <div></div>
89 - </li>
90 - <li class="tie-icon-add-button">
91 - <div class="tui-image-editor-button" style="margin:0">
92 - <div>
93 - <input type="file" accept="image/*" class="tie-icon-image-file">
94 - ${makeSvgIcon(['normal', 'active'], 'icon-load', true)}
95 - </div>
96 - <label>
97 - ${locale.localize('Custom icon')}
98 - </label>
99 - </div>
100 - </li>
101 - <li class="tui-image-editor-partition">
102 - <div></div>
103 - </li>
104 - <li>
105 - <div class="tie-icon-color" title="${locale.localize('Color')}"></div>
106 - </li>
107 - </ul>
108 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li>
10 - <div class="tui-image-editor-button">
11 - <div>
12 - <input type="file" accept="image/*" class="tie-mask-image-file">
13 - ${makeSvgIcon(['normal', 'active'], 'mask-load', true)}
14 - </div>
15 - <label> ${locale.localize('Load Mask Image')} </label>
16 - </div>
17 - </li>
18 - <li class="tui-image-editor-partition only-left-right">
19 - <div></div>
20 - </li>
21 - <li class="tie-mask-apply tui-image-editor-newline apply" style="margin-top: 22px;margin-bottom: 5px">
22 - <div class="tui-image-editor-button apply">
23 - ${makeSvgIcon(['normal', 'active'], 'apply')}
24 - <label>
25 - ${locale.localize('Apply')}
26 - </label>
27 - </div>
28 - </li>
29 - </ul>
30 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-retate-button">
10 - <div class="tui-image-editor-button clockwise">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'rotate-clockwise', true)}
13 - </div>
14 - <label> 30 </label>
15 - </div>
16 - <div class="tui-image-editor-button counterclockwise">
17 - <div>
18 - ${makeSvgIcon(['normal', 'active'], 'rotate-counterclockwise', true)}
19 - </div>
20 - <label> -30 </label>
21 - </div>
22 - </li>
23 - <li class="tui-image-editor-partition only-left-right">
24 - <div></div>
25 - </li>
26 - <li class="tui-image-editor-newline tui-image-editor-range-wrap">
27 - <label class="range">${locale.localize('Range')}</label>
28 - <div class="tie-rotate-range"></div>
29 - <input class="tie-ratate-range-value tui-image-editor-range-value" value="0" />
30 - </li>
31 - </ul>
32 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-shape-button">
10 - <div class="tui-image-editor-button rect">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'shape-rectangle', true)}
13 - </div>
14 - <label> ${locale.localize('Rectangle')} </label>
15 - </div>
16 - <div class="tui-image-editor-button circle">
17 - <div>
18 - ${makeSvgIcon(['normal', 'active'], 'shape-circle', true)}
19 - </div>
20 - <label> ${locale.localize('Circle')} </label>
21 - </div>
22 - <div class="tui-image-editor-button triangle">
23 - <div>
24 - ${makeSvgIcon(['normal', 'active'], 'shape-triangle', true)}
25 - </div>
26 - <label> ${locale.localize('Triangle')} </label>
27 - </div>
28 - </li>
29 - <li class="tui-image-editor-partition">
30 - <div></div>
31 - </li>
32 - <li class="tie-shape-color-button">
33 - <div class="tie-color-fill" title="${locale.localize('Fill')}"></div>
34 - <div class="tie-color-stroke" title="${locale.localize('Stroke')}"></div>
35 - </li>
36 - <li class="tui-image-editor-partition only-left-right">
37 - <div></div>
38 - </li>
39 - <li class="tui-image-editor-newline tui-image-editor-range-wrap">
40 - <label class="range">${locale.localize('Stroke')}</label>
41 - <div class="tie-stroke-range"></div>
42 - <input class="tie-stroke-range-value tui-image-editor-range-value" value="0" />
43 - </li>
44 - </ul>
45 -`;
1 -/**
2 - * @param {Object} submenuInfo - submenu info for make template
3 - * @param {Locale} locale - Translate text
4 - * @param {Function} makeSvgIcon - svg icon generator
5 - * @returns {string}
6 - */
7 -export default ({ locale, makeSvgIcon }) => `
8 - <ul class="tui-image-editor-submenu-item">
9 - <li class="tie-text-effect-button">
10 - <div class="tui-image-editor-button bold">
11 - <div>
12 - ${makeSvgIcon(['normal', 'active'], 'text-bold', true)}
13 - </div>
14 - <label> ${locale.localize('Bold')} </label>
15 - </div>
16 - <div class="tui-image-editor-button italic">
17 - <div>
18 - ${makeSvgIcon(['normal', 'active'], 'text-italic', true)}
19 - </div>
20 - <label> ${locale.localize('Italic')} </label>
21 - </div>
22 - <div class="tui-image-editor-button underline">
23 - <div>
24 - ${makeSvgIcon(['normal', 'active'], 'text-underline', true)}
25 - </div>
26 - <label> ${locale.localize('Underline')} </label>
27 - </div>
28 - </li>
29 - <li class="tui-image-editor-partition">
30 - <div></div>
31 - </li>
32 - <li class="tie-text-align-button">
33 - <div class="tui-image-editor-button left">
34 - <div>
35 - ${makeSvgIcon(['normal', 'active'], 'text-align-left', true)}
36 - </div>
37 - <label> ${locale.localize('Left')} </label>
38 - </div>
39 - <div class="tui-image-editor-button center">
40 - <div>
41 - ${makeSvgIcon(['normal', 'active'], 'text-align-center', true)}
42 - </div>
43 - <label> ${locale.localize('Center')} </label>
44 - </div>
45 - <div class="tui-image-editor-button right">
46 - <div>
47 - ${makeSvgIcon(['normal', 'active'], 'text-align-right', true)}
48 - </div>
49 - <label> ${locale.localize('Right')} </label>
50 - </div>
51 - </li>
52 - <li class="tui-image-editor-partition">
53 - <div></div>
54 - </li>
55 - <li>
56 - <div class="tie-text-color" title="${locale.localize('Color')}"></div>
57 - </li>
58 - <li class="tui-image-editor-partition only-left-right">
59 - <div></div>
60 - </li>
61 - <li class="tui-image-editor-newline tui-image-editor-range-wrap">
62 - <label class="range">${locale.localize('Text size')}</label>
63 - <div class="tie-text-range"></div>
64 - <input class="tie-text-range-value tui-image-editor-range-value" value="0" />
65 - </li>
66 - </ul>
67 -`;
1 -import { assignmentForDestroy } from '../util';
2 -import Range from './tools/range';
3 -import Colorpicker from './tools/colorpicker';
4 -import Submenu from './submenuBase';
5 -import templateHtml from './template/submenu/text';
6 -import { defaultTextRangeValus } from '../consts';
7 -
8 -/**
9 - * Crop ui class
10 - * @class
11 - * @ignore
12 - */
13 -export default class Text extends Submenu {
14 - constructor(subMenuElement, { locale, makeSvgIcon, menuBarPosition, usageStatistics }) {
15 - super(subMenuElement, {
16 - locale,
17 - name: 'text',
18 - makeSvgIcon,
19 - menuBarPosition,
20 - templateHtml,
21 - usageStatistics,
22 - });
23 - this.effect = {
24 - bold: false,
25 - italic: false,
26 - underline: false,
27 - };
28 - this.align = 'left';
29 - this._els = {
30 - textEffectButton: this.selector('.tie-text-effect-button'),
31 - textAlignButton: this.selector('.tie-text-align-button'),
32 - textColorpicker: new Colorpicker(
33 - this.selector('.tie-text-color'),
34 - '#ffbb3b',
35 - this.toggleDirection,
36 - this.usageStatistics
37 - ),
38 - textRange: new Range(
39 - {
40 - slider: this.selector('.tie-text-range'),
41 - input: this.selector('.tie-text-range-value'),
42 - },
43 - defaultTextRangeValus
44 - ),
45 - };
46 - }
47 -
48 - /**
49 - * Destroys the instance.
50 - */
51 - destroy() {
52 - this._removeEvent();
53 - this._els.textColorpicker.destroy();
54 - this._els.textRange.destroy();
55 -
56 - assignmentForDestroy(this);
57 - }
58 -
59 - /**
60 - * Add event for text
61 - * @param {Object} actions - actions for text
62 - * @param {Function} actions.changeTextStyle - change text style
63 - */
64 - addEvent(actions) {
65 - const setTextEffect = this._setTextEffectHandler.bind(this);
66 - const setTextAlign = this._setTextAlignHandler.bind(this);
67 -
68 - this.eventHandler = {
69 - setTextEffect,
70 - setTextAlign,
71 - };
72 -
73 - this.actions = actions;
74 - this._els.textEffectButton.addEventListener('click', setTextEffect);
75 - this._els.textAlignButton.addEventListener('click', setTextAlign);
76 - this._els.textRange.on('change', this._changeTextRnageHandler.bind(this));
77 - this._els.textColorpicker.on('change', this._changeColorHandler.bind(this));
78 - }
79 -
80 - /**
81 - * Remove event
82 - * @private
83 - */
84 - _removeEvent() {
85 - const { setTextEffect, setTextAlign } = this.eventHandler;
86 -
87 - this._els.textEffectButton.removeEventListener('click', setTextEffect);
88 - this._els.textAlignButton.removeEventListener('click', setTextAlign);
89 - this._els.textRange.off();
90 - this._els.textColorpicker.off();
91 - }
92 -
93 - /**
94 - * Returns the menu to its default state.
95 - */
96 - changeStandbyMode() {
97 - this.actions.stopDrawingMode();
98 - }
99 -
100 - /**
101 - * Executed when the menu starts.
102 - */
103 - changeStartMode() {
104 - this.actions.modeChange('text');
105 - }
106 -
107 - set textColor(color) {
108 - this._els.textColorpicker.color = color;
109 - }
110 -
111 - /**
112 - * Get text color
113 - * @returns {string} - text color
114 - */
115 - get textColor() {
116 - return this._els.textColorpicker.color;
117 - }
118 -
119 - /**
120 - * Get text size
121 - * @returns {string} - text size
122 - */
123 - get fontSize() {
124 - return this._els.textRange.value;
125 - }
126 -
127 - /**
128 - * Set text size
129 - * @param {Number} value - text size
130 - */
131 - set fontSize(value) {
132 - this._els.textRange.value = value;
133 - }
134 -
135 - /**
136 - * get font style
137 - * @returns {string} - font style
138 - */
139 - get fontStyle() {
140 - return this.effect.italic ? 'italic' : 'normal';
141 - }
142 -
143 - /**
144 - * get font weight
145 - * @returns {string} - font weight
146 - */
147 - get fontWeight() {
148 - return this.effect.bold ? 'bold' : 'normal';
149 - }
150 -
151 - /**
152 - * get text underline text underline
153 - * @returns {boolean} - true or false
154 - */
155 - get underline() {
156 - return this.effect.underline;
157 - }
158 -
159 - setTextStyleStateOnAction(textStyle = {}) {
160 - const { fill, fontSize, fontStyle, fontWeight, textDecoration, textAlign } = textStyle;
161 -
162 - this.textColor = fill;
163 - this.fontSize = fontSize;
164 - this.setEffactState('italic', fontStyle);
165 - this.setEffactState('bold', fontWeight);
166 - this.setEffactState('underline', textDecoration);
167 - this.setAlignState(textAlign);
168 - }
169 -
170 - setEffactState(effactName, value) {
171 - const effactValue = value === 'italic' || value === 'bold' || value === 'underline';
172 - const button = this._els.textEffectButton.querySelector(
173 - `.tui-image-editor-button.${effactName}`
174 - );
175 -
176 - this.effect[effactName] = effactValue;
177 -
178 - button.classList[effactValue ? 'add' : 'remove']('active');
179 - }
180 -
181 - setAlignState(value) {
182 - const button = this._els.textAlignButton;
183 - button.classList.remove(this.align);
184 - button.classList.add(value);
185 - this.align = value;
186 - }
187 -
188 - /**
189 - * text effect set handler
190 - * @param {object} event - add button event object
191 - * @private
192 - */
193 - _setTextEffectHandler(event) {
194 - const button = event.target.closest('.tui-image-editor-button');
195 - const [styleType] = button.className.match(/(bold|italic|underline)/);
196 - const styleObj = {
197 - bold: { fontWeight: 'bold' },
198 - italic: { fontStyle: 'italic' },
199 - underline: { textDecoration: 'underline' },
200 - }[styleType];
201 -
202 - this.effect[styleType] = !this.effect[styleType];
203 - button.classList.toggle('active');
204 - this.actions.changeTextStyle(styleObj);
205 - }
206 -
207 - /**
208 - * text effect set handler
209 - * @param {object} event - add button event object
210 - * @private
211 - */
212 - _setTextAlignHandler(event) {
213 - const button = event.target.closest('.tui-image-editor-button');
214 - if (button) {
215 - const styleType = this.getButtonType(button, ['left', 'center', 'right']);
216 -
217 - event.currentTarget.classList.remove(this.align);
218 - if (this.align !== styleType) {
219 - event.currentTarget.classList.add(styleType);
220 - }
221 - this.actions.changeTextStyle({ textAlign: styleType });
222 -
223 - this.align = styleType;
224 - }
225 - }
226 -
227 - /**
228 - * text align set handler
229 - * @param {number} value - range value
230 - * @param {boolean} isLast - Is last change
231 - * @private
232 - */
233 - _changeTextRnageHandler(value, isLast) {
234 - this.actions.changeTextStyle(
235 - {
236 - fontSize: value,
237 - },
238 - !isLast
239 - );
240 - }
241 -
242 - /**
243 - * change color handler
244 - * @param {string} color - change color string
245 - * @private
246 - */
247 - _changeColorHandler(color) {
248 - color = color || 'transparent';
249 - this.actions.changeTextStyle({
250 - fill: color,
251 - });
252 - }
253 -}
1 -/**
2 - * @fileoverview The standard theme
3 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
4 - */
5 -
6 -/**
7 - * Full configuration for theme.<br>
8 - * @typedef {object} themeConfig
9 - * @property {string} common.bi.image - Brand icon image
10 - * @property {string} common.bisize.width - Icon image width
11 - * @property {string} common.bisize.height - Icon Image Height
12 - * @property {string} common.backgroundImage - Background image
13 - * @property {string} common.backgroundColor - Background color
14 - * @property {string} common.border - Full area border style
15 - * @property {string} header.backgroundImage - header area background
16 - * @property {string} header.backgroundColor - header area background color
17 - * @property {string} header.border - header area border style
18 - * @property {string} loadButton.backgroundColor - load button background color
19 - * @property {string} loadButton.border - load button border style
20 - * @property {string} loadButton.color - load button foreground color
21 - * @property {string} loadButton.fontFamily - load button font type
22 - * @property {string} loadButton.fontSize - load button font size
23 - * @property {string} downloadButton.backgroundColor - download button background color
24 - * @property {string} downloadButton.border - download button border style
25 - * @property {string} downloadButton.color - download button foreground color
26 - * @property {string} downloadButton.fontFamily - download button font type
27 - * @property {string} downloadButton.fontSize - download button font size
28 - * @property {string} menu.normalIcon.color - Menu normal color for default icon
29 - * @property {string} menu.normalIcon.path - Menu normal icon svg bundle file path
30 - * @property {string} menu.normalIcon.name - Menu normal icon svg bundle name
31 - * @property {string} menu.activeIcon.color - Menu active color for default icon
32 - * @property {string} menu.activeIcon.path - Menu active icon svg bundle file path
33 - * @property {string} menu.activeIcon.name - Menu active icon svg bundle name
34 - * @property {string} menu.disabled.color - Menu disabled color for default icon
35 - * @property {string} menu.disabled.path - Menu disabled icon svg bundle file path
36 - * @property {string} menu.disabled.name - Menu disabled icon svg bundle name
37 - * @property {string} menu.hover.color - Menu default icon hover color
38 - * @property {string} menu.hover.path - Menu hover icon svg bundle file path
39 - * @property {string} menu.hover.name - Menu hover icon svg bundle name
40 - * @property {string} menu.iconSize.width - Menu icon Size Width
41 - * @property {string} menu.iconSize.height - Menu Icon Size Height
42 - * @property {string} submenu.backgroundColor - Sub-menu area background color
43 - * @property {string} submenu.partition.color - Submenu partition line color
44 - * @property {string} submenu.normalIcon.color - Submenu normal color for default icon
45 - * @property {string} submenu.normalIcon.path - Submenu default icon svg bundle file path
46 - * @property {string} submenu.normalIcon.name - Submenu default icon svg bundle name
47 - * @property {string} submenu.activeIcon.color - Submenu active color for default icon
48 - * @property {string} submenu.activeIcon.path - Submenu active icon svg bundle file path
49 - * @property {string} submenu.activeIcon.name - Submenu active icon svg bundle name
50 - * @property {string} submenu.iconSize.width - Submenu icon Size Width
51 - * @property {string} submenu.iconSize.height - Submenu Icon Size Height
52 - * @property {string} submenu.normalLabel.color - Submenu default label color
53 - * @property {string} submenu.normalLabel.fontWeight - Sub Menu Default Label Font Thickness
54 - * @property {string} submenu.activeLabel.color - Submenu active label color
55 - * @property {string} submenu.activeLabel.fontWeight - Submenu active label Font thickness
56 - * @property {string} checkbox.border - Checkbox border style
57 - * @property {string} checkbox.backgroundColor - Checkbox background color
58 - * @property {string} range.pointer.color - range control pointer color
59 - * @property {string} range.bar.color - range control bar color
60 - * @property {string} range.subbar.color - range control subbar color
61 - * @property {string} range.value.color - range number box font color
62 - * @property {string} range.value.fontWeight - range number box font thickness
63 - * @property {string} range.value.fontSize - range number box font size
64 - * @property {string} range.value.border - range number box border style
65 - * @property {string} range.value.backgroundColor - range number box background color
66 - * @property {string} range.title.color - range title font color
67 - * @property {string} range.title.fontWeight - range title font weight
68 - * @property {string} colorpicker.button.border - colorpicker button border style
69 - * @property {string} colorpicker.title.color - colorpicker button title font color
70 - * @example
71 - // default keys and styles
72 - var customTheme = {
73 - 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png',
74 - 'common.bisize.width': '251px',
75 - 'common.bisize.height': '21px',
76 - 'common.backgroundImage': 'none',
77 - 'common.backgroundColor': '#1e1e1e',
78 - 'common.border': '0px',
79 -
80 - // header
81 - 'header.backgroundImage': 'none',
82 - 'header.backgroundColor': 'transparent',
83 - 'header.border': '0px',
84 -
85 - // load button
86 - 'loadButton.backgroundColor': '#fff',
87 - 'loadButton.border': '1px solid #ddd',
88 - 'loadButton.color': '#222',
89 - 'loadButton.fontFamily': 'NotoSans, sans-serif',
90 - 'loadButton.fontSize': '12px',
91 -
92 - // download button
93 - 'downloadButton.backgroundColor': '#fdba3b',
94 - 'downloadButton.border': '1px solid #fdba3b',
95 - 'downloadButton.color': '#fff',
96 - 'downloadButton.fontFamily': 'NotoSans, sans-serif',
97 - 'downloadButton.fontSize': '12px',
98 -
99 - // icons default
100 - 'menu.normalIcon.color': '#8a8a8a',
101 - 'menu.activeIcon.color': '#555555',
102 - 'menu.disabledIcon.color': '#434343',
103 - 'menu.hoverIcon.color': '#e9e9e9',
104 - 'submenu.normalIcon.color': '#8a8a8a',
105 - 'submenu.activeIcon.color': '#e9e9e9',
106 -
107 - 'menu.iconSize.width': '24px',
108 - 'menu.iconSize.height': '24px',
109 - 'submenu.iconSize.width': '32px',
110 - 'submenu.iconSize.height': '32px',
111 -
112 - // submenu primary color
113 - 'submenu.backgroundColor': '#1e1e1e',
114 - 'submenu.partition.color': '#858585',
115 -
116 - // submenu labels
117 - 'submenu.normalLabel.color': '#858585',
118 - 'submenu.normalLabel.fontWeight': 'lighter',
119 - 'submenu.activeLabel.color': '#fff',
120 - 'submenu.activeLabel.fontWeight': 'lighter',
121 -
122 - // checkbox style
123 - 'checkbox.border': '1px solid #ccc',
124 - 'checkbox.backgroundColor': '#fff',
125 -
126 - // rango style
127 - 'range.pointer.color': '#fff',
128 - 'range.bar.color': '#666',
129 - 'range.subbar.color': '#d1d1d1',
130 -
131 - 'range.disabledPointer.color': '#414141',
132 - 'range.disabledBar.color': '#282828',
133 - 'range.disabledSubbar.color': '#414141',
134 -
135 - 'range.value.color': '#fff',
136 - 'range.value.fontWeight': 'lighter',
137 - 'range.value.fontSize': '11px',
138 - 'range.value.border': '1px solid #353535',
139 - 'range.value.backgroundColor': '#151515',
140 - 'range.title.color': '#fff',
141 - 'range.title.fontWeight': 'lighter',
142 -
143 - // colorpicker style
144 - 'colorpicker.button.border': '1px solid #1e1e1e',
145 - 'colorpicker.title.color': '#fff'
146 -};
147 - */
148 -export default {
149 - 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png',
150 - 'common.bisize.width': '251px',
151 - 'common.bisize.height': '21px',
152 - 'common.backgroundImage': 'none',
153 - 'common.backgroundColor': '#1e1e1e',
154 - 'common.border': '0px',
155 -
156 - // header
157 - 'header.backgroundImage': 'none',
158 - 'header.backgroundColor': 'transparent',
159 - 'header.border': '0px',
160 -
161 - // load button
162 - 'loadButton.backgroundColor': '#fff',
163 - 'loadButton.border': '1px solid #ddd',
164 - 'loadButton.color': '#222',
165 - 'loadButton.fontFamily': "'Noto Sans', sans-serif",
166 - 'loadButton.fontSize': '12px',
167 -
168 - // download button
169 - 'downloadButton.backgroundColor': '#fdba3b',
170 - 'downloadButton.border': '1px solid #fdba3b',
171 - 'downloadButton.color': '#fff',
172 - 'downloadButton.fontFamily': "'Noto Sans', sans-serif",
173 - 'downloadButton.fontSize': '12px',
174 -
175 - // main icons
176 - 'menu.normalIcon.color': '#8a8a8a',
177 - 'menu.activeIcon.color': '#555555',
178 - 'menu.disabledIcon.color': '#434343',
179 - 'menu.hoverIcon.color': '#e9e9e9',
180 -
181 - // submenu icons
182 - 'submenu.normalIcon.color': '#8a8a8a',
183 - 'submenu.activeIcon.color': '#e9e9e9',
184 -
185 - 'menu.iconSize.width': '24px',
186 - 'menu.iconSize.height': '24px',
187 -
188 - 'submenu.iconSize.width': '32px',
189 - 'submenu.iconSize.height': '32px',
190 -
191 - // submenu primary color
192 - 'submenu.backgroundColor': '#1e1e1e',
193 - 'submenu.partition.color': '#3c3c3c',
194 -
195 - // submenu labels
196 - 'submenu.normalLabel.color': '#8a8a8a',
197 - 'submenu.normalLabel.fontWeight': 'lighter',
198 - 'submenu.activeLabel.color': '#fff',
199 - 'submenu.activeLabel.fontWeight': 'lighter',
200 -
201 - // checkbox style
202 - 'checkbox.border': '0px',
203 - 'checkbox.backgroundColor': '#fff',
204 -
205 - // range style
206 - 'range.pointer.color': '#fff',
207 - 'range.bar.color': '#666',
208 - 'range.subbar.color': '#d1d1d1',
209 -
210 - 'range.disabledPointer.color': '#414141',
211 - 'range.disabledBar.color': '#282828',
212 - 'range.disabledSubbar.color': '#414141',
213 -
214 - 'range.value.color': '#fff',
215 - 'range.value.fontWeight': 'lighter',
216 - 'range.value.fontSize': '11px',
217 - 'range.value.border': '1px solid #353535',
218 - 'range.value.backgroundColor': '#151515',
219 - 'range.title.color': '#fff',
220 - 'range.title.fontWeight': 'lighter',
221 -
222 - // colorpicker style
223 - 'colorpicker.button.border': '1px solid #1e1e1e',
224 - 'colorpicker.title.color': '#fff',
225 -};
1 -import { extend, forEach, map } from 'tui-code-snippet';
2 -import { styleLoad } from '../../util';
3 -import style from '../template/style';
4 -import standardTheme from './standard';
5 -import icon from '../../../svg/default.svg';
6 -
7 -/**
8 - * Theme manager
9 - * @class
10 - * @param {Object} customTheme - custom theme
11 - * @ignore
12 - */
13 -class Theme {
14 - constructor(customTheme) {
15 - this.styles = this._changeToObject(extend({}, standardTheme, customTheme));
16 - styleLoad(this._styleMaker());
17 -
18 - this._loadDefaultSvgIcon();
19 - }
20 -
21 - /**
22 - * Get a Style cssText or StyleObject
23 - * @param {string} type - style type
24 - * @returns {string|object} - cssText or StyleObject
25 - */
26 - // eslint-disable-next-line complexity
27 - getStyle(type) {
28 - let result = null;
29 - const firstProperty = type.replace(/\..+$/, '');
30 - const option = this.styles[type];
31 - switch (type) {
32 - case 'common.bi':
33 - result = this.styles[type].image;
34 - break;
35 - case 'menu.icon':
36 - result = {
37 - active: this.styles[`${firstProperty}.activeIcon`],
38 - normal: this.styles[`${firstProperty}.normalIcon`],
39 - hover: this.styles[`${firstProperty}.hoverIcon`],
40 - disabled: this.styles[`${firstProperty}.disabledIcon`],
41 - };
42 - break;
43 - case 'submenu.icon':
44 - result = {
45 - active: this.styles[`${firstProperty}.activeIcon`],
46 - normal: this.styles[`${firstProperty}.normalIcon`],
47 - };
48 - break;
49 - case 'submenu.label':
50 - result = {
51 - active: this._makeCssText(this.styles[`${firstProperty}.activeLabel`]),
52 - normal: this._makeCssText(this.styles[`${firstProperty}.normalLabel`]),
53 - };
54 - break;
55 - case 'submenu.partition':
56 - result = {
57 - vertical: this._makeCssText(
58 - extend({}, option, { borderLeft: `1px solid ${option.color}` })
59 - ),
60 - horizontal: this._makeCssText(
61 - extend({}, option, { borderBottom: `1px solid ${option.color}` })
62 - ),
63 - };
64 - break;
65 -
66 - case 'range.disabledPointer':
67 - case 'range.disabledBar':
68 - case 'range.disabledSubbar':
69 - case 'range.pointer':
70 - case 'range.bar':
71 - case 'range.subbar':
72 - option.backgroundColor = option.color;
73 - result = this._makeCssText(option);
74 - break;
75 - default:
76 - result = this._makeCssText(option);
77 - break;
78 - }
79 -
80 - return result;
81 - }
82 -
83 - /**
84 - * Make css resource
85 - * @returns {string} - serialized css text
86 - * @private
87 - */
88 - _styleMaker() {
89 - const submenuLabelStyle = this.getStyle('submenu.label');
90 - const submenuPartitionStyle = this.getStyle('submenu.partition');
91 -
92 - return style({
93 - subMenuLabelActive: submenuLabelStyle.active,
94 - subMenuLabelNormal: submenuLabelStyle.normal,
95 - submenuPartitionVertical: submenuPartitionStyle.vertical,
96 - submenuPartitionHorizontal: submenuPartitionStyle.horizontal,
97 - biSize: this.getStyle('common.bisize'),
98 - subMenuRangeTitle: this.getStyle('range.title'),
99 - submenuRangePointer: this.getStyle('range.pointer'),
100 - submenuRangeBar: this.getStyle('range.bar'),
101 - submenuRangeSubbar: this.getStyle('range.subbar'),
102 -
103 - submenuDisabledRangePointer: this.getStyle('range.disabledPointer'),
104 - submenuDisabledRangeBar: this.getStyle('range.disabledBar'),
105 - submenuDisabledRangeSubbar: this.getStyle('range.disabledSubbar'),
106 -
107 - submenuRangeValue: this.getStyle('range.value'),
108 - submenuColorpickerTitle: this.getStyle('colorpicker.title'),
109 - submenuColorpickerButton: this.getStyle('colorpicker.button'),
110 - submenuCheckbox: this.getStyle('checkbox'),
111 - menuIconSize: this.getStyle('menu.iconSize'),
112 - submenuIconSize: this.getStyle('submenu.iconSize'),
113 - menuIconStyle: this.getStyle('menu.icon'),
114 - submenuIconStyle: this.getStyle('submenu.icon'),
115 - });
116 - }
117 -
118 - /**
119 - * Change to low dimensional object.
120 - * @param {object} styleOptions - style object of user interface
121 - * @returns {object} low level object for style apply
122 - * @private
123 - */
124 - _changeToObject(styleOptions) {
125 - const styleObject = {};
126 - forEach(styleOptions, (value, key) => {
127 - const keyExplode = key.match(/^(.+)\.([a-z]+)$/i);
128 - const [, property, subProperty] = keyExplode;
129 -
130 - if (!styleObject[property]) {
131 - styleObject[property] = {};
132 - }
133 - styleObject[property][subProperty] = value;
134 - });
135 -
136 - return styleObject;
137 - }
138 -
139 - /**
140 - * Style object to Csstext serialize
141 - * @param {object} styleObject - style object
142 - * @returns {string} - css text string
143 - * @private
144 - */
145 - _makeCssText(styleObject) {
146 - const converterStack = [];
147 -
148 - forEach(styleObject, (value, key) => {
149 - if (['backgroundImage'].indexOf(key) > -1 && value !== 'none') {
150 - value = `url(${value})`;
151 - }
152 -
153 - converterStack.push(`${this._toUnderScore(key)}: ${value}`);
154 - });
155 -
156 - return converterStack.join(';');
157 - }
158 -
159 - /**
160 - * Camel key string to Underscore string
161 - * @param {string} targetString - change target
162 - * @returns {string}
163 - * @private
164 - */
165 - _toUnderScore(targetString) {
166 - return targetString.replace(/([A-Z])/g, ($0, $1) => `-${$1.toLowerCase()}`);
167 - }
168 -
169 - /**
170 - * Load defulat svg icon
171 - * @private
172 - */
173 - _loadDefaultSvgIcon() {
174 - if (!document.getElementById('tui-image-editor-svg-default-icons')) {
175 - const parser = new DOMParser();
176 - const dom = parser.parseFromString(icon, 'text/xml');
177 -
178 - document.body.appendChild(dom.documentElement);
179 - }
180 - }
181 -
182 - /**
183 - * Make className for svg icon
184 - * @param {string} iconType - normal' or 'active' or 'hover' or 'disabled
185 - * @param {boolean} isSubmenu - submenu icon or not.
186 - * @returns {string}
187 - * @private
188 - */
189 - _makeIconClassName(iconType, isSubmenu) {
190 - const iconStyleInfo = isSubmenu ? this.getStyle('submenu.icon') : this.getStyle('menu.icon');
191 - const { path, name } = iconStyleInfo[iconType];
192 -
193 - return path && name ? iconType : `${iconType} use-default`;
194 - }
195 -
196 - /**
197 - * Make svg use link path name
198 - * @param {string} iconType - normal' or 'active' or 'hover' or 'disabled
199 - * @param {boolean} isSubmenu - submenu icon or not.
200 - * @returns {string}
201 - * @private
202 - */
203 - _makeSvgIconPrefix(iconType, isSubmenu) {
204 - const iconStyleInfo = isSubmenu ? this.getStyle('submenu.icon') : this.getStyle('menu.icon');
205 - const { path, name } = iconStyleInfo[iconType];
206 -
207 - return path && name ? `${path}#${name}-` : '#';
208 - }
209 -
210 - /**
211 - * Make svg use link path name
212 - * @param {Array.<string>} useIconTypes - normal' or 'active' or 'hover' or 'disabled
213 - * @param {string} menuName - menu name
214 - * @param {boolean} isSubmenu - submenu icon or not.
215 - * @returns {string}
216 - * @private
217 - */
218 - _makeSvgItem(useIconTypes, menuName, isSubmenu) {
219 - return map(useIconTypes, (iconType) => {
220 - const svgIconPrefix = this._makeSvgIconPrefix(iconType, isSubmenu);
221 - const iconName = this._toUnderScore(menuName);
222 - const svgIconClassName = this._makeIconClassName(iconType, isSubmenu);
223 -
224 - return `<use xlink:href="${svgIconPrefix}ic-${iconName}" class="${svgIconClassName}"/>`;
225 - }).join('');
226 - }
227 -
228 - /**
229 - * Make svg icon set
230 - * @param {Array.<string>} useIconTypes - normal' or 'active' or 'hover' or 'disabled
231 - * @param {string} menuName - menu name
232 - * @param {boolean} isSubmenu - submenu icon or not.
233 - * @returns {string}
234 - */
235 - makeMenSvgIconSet(useIconTypes, menuName, isSubmenu = false) {
236 - return `<svg class="svg_ic-${isSubmenu ? 'submenu' : 'menu'}">${this._makeSvgItem(
237 - useIconTypes,
238 - menuName,
239 - isSubmenu
240 - )}</svg>`;
241 - }
242 -}
243 -
244 -export default Theme;
1 -import snippet from 'tui-code-snippet';
2 -import tuiColorPicker from 'tui-color-picker';
3 -const PICKER_COLOR = [
4 - '#000000',
5 - '#2a2a2a',
6 - '#545454',
7 - '#7e7e7e',
8 - '#a8a8a8',
9 - '#d2d2d2',
10 - '#ffffff',
11 - '',
12 - '#ff4040',
13 - '#ff6518',
14 - '#ffbb3b',
15 - '#03bd9e',
16 - '#00a9ff',
17 - '#515ce6',
18 - '#9e5fff',
19 - '#ff5583',
20 -];
21 -
22 -/**
23 - * Colorpicker control class
24 - * @class
25 - * @ignore
26 - */
27 -class Colorpicker {
28 - constructor(
29 - colorpickerElement,
30 - defaultColor = '#7e7e7e',
31 - toggleDirection = 'up',
32 - usageStatistics
33 - ) {
34 - this.colorpickerElement = colorpickerElement;
35 - this.usageStatistics = usageStatistics;
36 -
37 - this._show = false;
38 -
39 - this._colorpickerElement = colorpickerElement;
40 - this._toggleDirection = toggleDirection;
41 - this._makePickerButtonElement(defaultColor);
42 - this._makePickerLayerElement(colorpickerElement, colorpickerElement.getAttribute('title'));
43 - this._color = defaultColor;
44 - this.picker = tuiColorPicker.create({
45 - container: this.pickerElement,
46 - preset: PICKER_COLOR,
47 - color: defaultColor,
48 - usageStatistics: this.usageStatistics,
49 - });
50 -
51 - this._addEvent();
52 - }
53 -
54 - /**
55 - * Destroys the instance.
56 - */
57 - destroy() {
58 - this._removeEvent();
59 - this.picker.destroy();
60 - this.colorpickerElement.innerHTML = '';
61 - snippet.forEach(this, (value, key) => {
62 - this[key] = null;
63 - });
64 - }
65 -
66 - /**
67 - * Get color
68 - * @returns {Number} color value
69 - */
70 - get color() {
71 - return this._color;
72 - }
73 -
74 - /**
75 - * Set color
76 - * @param {string} color color value
77 - */
78 - set color(color) {
79 - this._color = color;
80 - this._changeColorElement(color);
81 - }
82 -
83 - /**
84 - * Change color element
85 - * @param {string} color color value
86 - * #private
87 - */
88 - _changeColorElement(color) {
89 - if (color) {
90 - this.colorElement.classList.remove('transparent');
91 - this.colorElement.style.backgroundColor = color;
92 - } else {
93 - this.colorElement.style.backgroundColor = '#fff';
94 - this.colorElement.classList.add('transparent');
95 - }
96 - }
97 -
98 - /**
99 - * Make picker button element
100 - * @param {string} defaultColor color value
101 - * @private
102 - */
103 - _makePickerButtonElement(defaultColor) {
104 - this.colorpickerElement.classList.add('tui-image-editor-button');
105 -
106 - this.colorElement = document.createElement('div');
107 - this.colorElement.className = 'color-picker-value';
108 - if (defaultColor) {
109 - this.colorElement.style.backgroundColor = defaultColor;
110 - } else {
111 - this.colorElement.classList.add('transparent');
112 - }
113 - }
114 -
115 - /**
116 - * Make picker layer element
117 - * @param {HTMLElement} colorpickerElement color picker element
118 - * @param {string} title picker title
119 - * @private
120 - */
121 - _makePickerLayerElement(colorpickerElement, title) {
122 - const label = document.createElement('label');
123 - const triangle = document.createElement('div');
124 -
125 - this.pickerControl = document.createElement('div');
126 - this.pickerControl.className = 'color-picker-control';
127 -
128 - this.pickerElement = document.createElement('div');
129 - this.pickerElement.className = 'color-picker';
130 -
131 - label.innerHTML = title;
132 - triangle.className = 'triangle';
133 -
134 - this.pickerControl.appendChild(this.pickerElement);
135 - this.pickerControl.appendChild(triangle);
136 -
137 - colorpickerElement.appendChild(this.pickerControl);
138 - colorpickerElement.appendChild(this.colorElement);
139 - colorpickerElement.appendChild(label);
140 - }
141 -
142 - /**
143 - * Add event
144 - * @private
145 - */
146 - _addEvent() {
147 - this.picker.on('selectColor', (value) => {
148 - this._changeColorElement(value.color);
149 - this._color = value.color;
150 - this.fire('change', value.color);
151 - });
152 -
153 - this.eventHandler = {
154 - pickerToggle: this._pickerToggleEventHandler.bind(this),
155 - pickerHide: () => this.hide(),
156 - };
157 -
158 - this.colorpickerElement.addEventListener('click', this.eventHandler.pickerToggle);
159 - document.body.addEventListener('click', this.eventHandler.pickerHide);
160 - }
161 -
162 - /**
163 - * Remove event
164 - * @private
165 - */
166 - _removeEvent() {
167 - this.colorpickerElement.removeEventListener('click', this.eventHandler.pickerToggle);
168 - document.body.removeEventListener('click', this.eventHandler.pickerHide);
169 - this.picker.off();
170 - }
171 -
172 - /**
173 - * Picker toggle event handler
174 - * @param {object} event - change event
175 - * @private
176 - */
177 - _pickerToggleEventHandler(event) {
178 - const { target } = event;
179 - const isInPickerControl = target && this._isElementInColorPickerControl(target);
180 -
181 - if (!isInPickerControl || (isInPickerControl && this._isPaletteButton(target))) {
182 - this._show = !this._show;
183 - this.pickerControl.style.display = this._show ? 'block' : 'none';
184 - this._setPickerControlPosition();
185 - this.fire('changeShow', this);
186 - }
187 - event.stopPropagation();
188 - }
189 -
190 - /**
191 - * Check hex input or not
192 - * @param {Element} target - Event target element
193 - * @returns {boolean}
194 - * @private
195 - */
196 - _isPaletteButton(target) {
197 - return target.className === 'tui-colorpicker-palette-button';
198 - }
199 -
200 - /**
201 - * Check given element is in pickerControl element
202 - * @param {Element} element - element to check
203 - * @returns {boolean}
204 - * @private
205 - */
206 - _isElementInColorPickerControl(element) {
207 - let parentNode = element;
208 -
209 - while (parentNode !== document.body) {
210 - if (!parentNode) {
211 - break;
212 - }
213 -
214 - if (parentNode === this.pickerControl) {
215 - return true;
216 - }
217 -
218 - parentNode = parentNode.parentNode;
219 - }
220 -
221 - return false;
222 - }
223 -
224 - hide() {
225 - this._show = false;
226 - this.pickerControl.style.display = 'none';
227 - }
228 -
229 - /**
230 - * Set picker control position
231 - * @private
232 - */
233 - _setPickerControlPosition() {
234 - const controlStyle = this.pickerControl.style;
235 - const halfPickerWidth = this._colorpickerElement.clientWidth / 2 + 2;
236 - const left = this.pickerControl.offsetWidth / 2 - halfPickerWidth;
237 - let top = (this.pickerControl.offsetHeight + 10) * -1;
238 -
239 - if (this._toggleDirection === 'down') {
240 - top = 30;
241 - }
242 -
243 - controlStyle.top = `${top}px`;
244 - controlStyle.left = `-${left}px`;
245 - }
246 -}
247 -
248 -snippet.CustomEvents.mixin(Colorpicker);
249 -export default Colorpicker;
1 -import snippet from 'tui-code-snippet';
2 -import { toInteger, clamp } from '../../util';
3 -import { keyCodes } from '../../consts';
4 -
5 -const INPUT_FILTER_REGEXP = /(-?)([0-9]*)[^0-9]*([0-9]*)/g;
6 -
7 -/**
8 - * Range control class
9 - * @class
10 - * @ignore
11 - */
12 -class Range {
13 - /**
14 - * @constructor
15 - * @extends {View}
16 - * @param {Object} rangeElements - Html resources for creating sliders
17 - * @param {HTMLElement} rangeElements.slider - b
18 - * @param {HTMLElement} [rangeElements.input] - c
19 - * @param {Object} options - Slider make options
20 - * @param {number} options.min - min value
21 - * @param {number} options.max - max value
22 - * @param {number} options.value - default value
23 - * @param {number} [options.useDecimal] - Decimal point processing.
24 - * @param {number} [options.realTimeEvent] - Reflect live events.
25 - */
26 - constructor(rangeElements, options = {}) {
27 - this._value = options.value || 0;
28 -
29 - this.rangeElement = rangeElements.slider;
30 - this.rangeInputElement = rangeElements.input;
31 -
32 - this._drawRangeElement();
33 -
34 - this.rangeWidth = this._getRangeWidth();
35 - this._min = options.min || 0;
36 - this._max = options.max || 100;
37 - this._useDecimal = options.useDecimal;
38 - this._absMax = this._min * -1 + this._max;
39 - this.realTimeEvent = options.realTimeEvent || false;
40 -
41 - this.eventHandler = {
42 - startChangingSlide: this._startChangingSlide.bind(this),
43 - stopChangingSlide: this._stopChangingSlide.bind(this),
44 - changeSlide: this._changeSlide.bind(this),
45 - changeSlideFinally: this._changeSlideFinally.bind(this),
46 - changeInput: this._changeValueWithInput.bind(this, false),
47 - changeInputFinally: this._changeValueWithInput.bind(this, true),
48 - changeInputWithArrow: this._changeValueWithInputKeyEvent.bind(this),
49 - };
50 -
51 - this._addClickEvent();
52 - this._addDragEvent();
53 - this._addInputEvent();
54 - this.value = options.value;
55 - this.trigger('change');
56 - }
57 -
58 - /**
59 - * Destroys the instance.
60 - */
61 - destroy() {
62 - this._removeClickEvent();
63 - this._removeDragEvent();
64 - this._removeInputEvent();
65 - this.rangeElement.innerHTML = '';
66 - snippet.forEach(this, (value, key) => {
67 - this[key] = null;
68 - });
69 - }
70 -
71 - /**
72 - * Set range max value and re position cursor
73 - * @param {number} maxValue - max value
74 - */
75 - set max(maxValue) {
76 - this._max = maxValue;
77 - this._absMax = this._min * -1 + this._max;
78 - this.value = this._value;
79 - }
80 -
81 - get max() {
82 - return this._max;
83 - }
84 -
85 - /**
86 - * Get range value
87 - * @returns {Number} range value
88 - */
89 - get value() {
90 - return this._value;
91 - }
92 -
93 - /**
94 - * Set range value
95 - * @param {Number} value range value
96 - * @param {Boolean} fire whether fire custom event or not
97 - */
98 - set value(value) {
99 - value = this._useDecimal ? value : toInteger(value);
100 -
101 - const absValue = value - this._min;
102 - let leftPosition = (absValue * this.rangeWidth) / this._absMax;
103 -
104 - if (this.rangeWidth < leftPosition) {
105 - leftPosition = this.rangeWidth;
106 - }
107 -
108 - this.pointer.style.left = `${leftPosition}px`;
109 - this.subbar.style.right = `${this.rangeWidth - leftPosition}px`;
110 -
111 - this._value = value;
112 - if (this.rangeInputElement) {
113 - this.rangeInputElement.value = value;
114 - }
115 - }
116 -
117 - /**
118 - * event tirigger
119 - * @param {string} type - type
120 - */
121 - trigger(type) {
122 - this.fire(type, this._value);
123 - }
124 -
125 - /**
126 - * Calculate slider width
127 - * @returns {number} - slider width
128 - */
129 - _getRangeWidth() {
130 - const getElementWidth = (element) => toInteger(window.getComputedStyle(element, null).width);
131 -
132 - return getElementWidth(this.rangeElement) - getElementWidth(this.pointer);
133 - }
134 -
135 - /**
136 - * Make range element
137 - * @private
138 - */
139 - _drawRangeElement() {
140 - this.rangeElement.classList.add('tui-image-editor-range');
141 -
142 - this.bar = document.createElement('div');
143 - this.bar.className = 'tui-image-editor-virtual-range-bar';
144 -
145 - this.subbar = document.createElement('div');
146 - this.subbar.className = 'tui-image-editor-virtual-range-subbar';
147 -
148 - this.pointer = document.createElement('div');
149 - this.pointer.className = 'tui-image-editor-virtual-range-pointer';
150 -
151 - this.bar.appendChild(this.subbar);
152 - this.bar.appendChild(this.pointer);
153 - this.rangeElement.appendChild(this.bar);
154 - }
155 -
156 - /**
157 - * Add range input editing event
158 - * @private
159 - */
160 - _addInputEvent() {
161 - if (this.rangeInputElement) {
162 - this.rangeInputElement.addEventListener('keydown', this.eventHandler.changeInputWithArrow);
163 - this.rangeInputElement.addEventListener('keyup', this.eventHandler.changeInput);
164 - this.rangeInputElement.addEventListener('blur', this.eventHandler.changeInputFinally);
165 - }
166 - }
167 -
168 - /**
169 - * Remove range input editing event
170 - * @private
171 - */
172 - _removeInputEvent() {
173 - if (this.rangeInputElement) {
174 - this.rangeInputElement.removeEventListener('keydown', this.eventHandler.changeInputWithArrow);
175 - this.rangeInputElement.removeEventListener('keyup', this.eventHandler.changeInput);
176 - this.rangeInputElement.removeEventListener('blur', this.eventHandler.changeInputFinally);
177 - }
178 - }
179 -
180 - /**
181 - * change angle event
182 - * @param {object} event - key event
183 - * @private
184 - */
185 - _changeValueWithInputKeyEvent(event) {
186 - const { keyCode, target } = event;
187 -
188 - if ([keyCodes.ARROW_UP, keyCodes.ARROW_DOWN].indexOf(keyCode) < 0) {
189 - return;
190 - }
191 -
192 - let value = Number(target.value);
193 -
194 - value = this._valueUpDownForKeyEvent(value, keyCode);
195 -
196 - const unChanged = value < this._min || value > this._max;
197 -
198 - if (!unChanged) {
199 - const clampValue = clamp(value, this._min, this.max);
200 - this.value = clampValue;
201 - this.fire('change', clampValue, false);
202 - }
203 - }
204 -
205 - /**
206 - * value up down for input
207 - * @param {number} value - original value number
208 - * @param {number} keyCode - input event key code
209 - * @returns {number} value - changed value
210 - * @private
211 - */
212 - _valueUpDownForKeyEvent(value, keyCode) {
213 - const step = this._useDecimal ? 0.1 : 1;
214 -
215 - if (keyCode === keyCodes.ARROW_UP) {
216 - value += step;
217 - } else if (keyCode === keyCodes.ARROW_DOWN) {
218 - value -= step;
219 - }
220 -
221 - return value;
222 - }
223 -
224 - /**
225 - * change angle event
226 - * @param {boolean} isLast - Is last change
227 - * @param {object} event - key event
228 - * @private
229 - */
230 - _changeValueWithInput(isLast, event) {
231 - const { keyCode, target } = event;
232 -
233 - if ([keyCodes.ARROW_UP, keyCodes.ARROW_DOWN].indexOf(keyCode) >= 0) {
234 - return;
235 - }
236 -
237 - const stringValue = this._filterForInputText(target.value);
238 - const waitForChange = !stringValue || isNaN(stringValue);
239 - target.value = stringValue;
240 -
241 - if (!waitForChange) {
242 - let value = this._useDecimal ? Number(stringValue) : toInteger(stringValue);
243 - value = clamp(value, this._min, this.max);
244 -
245 - this.value = value;
246 - this.fire('change', value, isLast);
247 - }
248 - }
249 -
250 - /**
251 - * Add Range click event
252 - * @private
253 - */
254 - _addClickEvent() {
255 - this.rangeElement.addEventListener('click', this.eventHandler.changeSlideFinally);
256 - }
257 -
258 - /**
259 - * Remove Range click event
260 - * @private
261 - */
262 - _removeClickEvent() {
263 - this.rangeElement.removeEventListener('click', this.eventHandler.changeSlideFinally);
264 - }
265 -
266 - /**
267 - * Add Range drag event
268 - * @private
269 - */
270 - _addDragEvent() {
271 - this.pointer.addEventListener('mousedown', this.eventHandler.startChangingSlide);
272 - }
273 -
274 - /**
275 - * Remove Range drag event
276 - * @private
277 - */
278 - _removeDragEvent() {
279 - this.pointer.removeEventListener('mousedown', this.eventHandler.startChangingSlide);
280 - }
281 -
282 - /**
283 - * change angle event
284 - * @param {object} event - change event
285 - * @private
286 - */
287 - _changeSlide(event) {
288 - const changePosition = event.screenX;
289 - const diffPosition = changePosition - this.firstPosition;
290 - let touchPx = this.firstLeft + diffPosition;
291 - touchPx = touchPx > this.rangeWidth ? this.rangeWidth : touchPx;
292 - touchPx = touchPx < 0 ? 0 : touchPx;
293 -
294 - this.pointer.style.left = `${touchPx}px`;
295 - this.subbar.style.right = `${this.rangeWidth - touchPx}px`;
296 -
297 - const ratio = touchPx / this.rangeWidth;
298 - const resultValue = this._absMax * ratio + this._min;
299 - const value = this._useDecimal ? resultValue : toInteger(resultValue);
300 - const isValueChanged = this.value !== value;
301 -
302 - if (isValueChanged) {
303 - this.value = value;
304 - if (this.realTimeEvent) {
305 - this.fire('change', this._value, false);
306 - }
307 - }
308 - }
309 -
310 - _changeSlideFinally(event) {
311 - event.stopPropagation();
312 - if (event.target.className !== 'tui-image-editor-range') {
313 - return;
314 - }
315 - const touchPx = event.offsetX;
316 - const ratio = touchPx / this.rangeWidth;
317 - const value = this._absMax * ratio + this._min;
318 - this.pointer.style.left = `${ratio * this.rangeWidth}px`;
319 - this.subbar.style.right = `${(1 - ratio) * this.rangeWidth}px`;
320 - this.value = value;
321 -
322 - this.fire('change', value, true);
323 - }
324 -
325 - _startChangingSlide(event) {
326 - this.firstPosition = event.screenX;
327 - this.firstLeft = toInteger(this.pointer.style.left) || 0;
328 -
329 - document.addEventListener('mousemove', this.eventHandler.changeSlide);
330 - document.addEventListener('mouseup', this.eventHandler.stopChangingSlide);
331 - }
332 -
333 - /**
334 - * stop change angle event
335 - * @private
336 - */
337 - _stopChangingSlide() {
338 - this.fire('change', this._value, true);
339 -
340 - document.removeEventListener('mousemove', this.eventHandler.changeSlide);
341 - document.removeEventListener('mouseup', this.eventHandler.stopChangingSlide);
342 - }
343 -
344 - /**
345 - * Unnecessary string filtering.
346 - * @param {string} inputValue - origin string of input
347 - * @returns {string} filtered string
348 - * @private
349 - */
350 - _filterForInputText(inputValue) {
351 - return inputValue.replace(INPUT_FILTER_REGEXP, '$1$2$3');
352 - }
353 -}
354 -
355 -snippet.CustomEvents.mixin(Range);
356 -export default Range;
1 -/**
2 - * @author NHN Ent. FE Development Team <dl_javascript@nhn.com>
3 - * @fileoverview Util
4 - */
5 -import { forEach, sendHostname, extend, isString, pick, inArray } from 'tui-code-snippet';
6 -import Promise from 'core-js-pure/features/promise';
7 -import { SHAPE_FILL_TYPE, SHAPE_TYPE } from './consts';
8 -const FLOATING_POINT_DIGIT = 2;
9 -const CSS_PREFIX = 'tui-image-editor-';
10 -const { min, max } = Math;
11 -let hostnameSent = false;
12 -
13 -/**
14 - * Export Promise Class (for simplified module path)
15 - * @returns {Promise} promise class
16 - */
17 -export { Promise };
18 -
19 -/**
20 - * Clamp value
21 - * @param {number} value - Value
22 - * @param {number} minValue - Minimum value
23 - * @param {number} maxValue - Maximum value
24 - * @returns {number} clamped value
25 - */
26 -export function clamp(value, minValue, maxValue) {
27 - let temp;
28 - if (minValue > maxValue) {
29 - temp = minValue;
30 - minValue = maxValue;
31 - maxValue = temp;
32 - }
33 -
34 - return max(minValue, min(value, maxValue));
35 -}
36 -
37 -/**
38 - * Make key-value object from arguments
39 - * @returns {object.<string, string>}
40 - */
41 -export function keyMirror(...args) {
42 - const obj = {};
43 -
44 - forEach(args, (key) => {
45 - obj[key] = key;
46 - });
47 -
48 - return obj;
49 -}
50 -
51 -/**
52 - * Make CSSText
53 - * @param {Object} styleObj - Style info object
54 - * @returns {string} Connected string of style
55 - */
56 -export function makeStyleText(styleObj) {
57 - let styleStr = '';
58 -
59 - forEach(styleObj, (value, prop) => {
60 - styleStr += `${prop}: ${value};`;
61 - });
62 -
63 - return styleStr;
64 -}
65 -
66 -/**
67 - * Get object's properties
68 - * @param {Object} obj - object
69 - * @param {Array} keys - keys
70 - * @returns {Object} properties object
71 - */
72 -export function getProperties(obj, keys) {
73 - const props = {};
74 - const { length } = keys;
75 - let i = 0;
76 - let key;
77 -
78 - for (i = 0; i < length; i += 1) {
79 - key = keys[i];
80 - props[key] = obj[key];
81 - }
82 -
83 - return props;
84 -}
85 -
86 -/**
87 - * ParseInt simpliment
88 - * @param {number} value - Value
89 - * @returns {number}
90 - */
91 -export function toInteger(value) {
92 - return parseInt(value, 10);
93 -}
94 -
95 -/**
96 - * String to camelcase string
97 - * @param {string} targetString - change target
98 - * @returns {string}
99 - * @private
100 - */
101 -export function toCamelCase(targetString) {
102 - return targetString.replace(/-([a-z])/g, ($0, $1) => $1.toUpperCase());
103 -}
104 -
105 -/**
106 - * Check browser file api support
107 - * @returns {boolean}
108 - * @private
109 - */
110 -export function isSupportFileApi() {
111 - return !!(window.File && window.FileList && window.FileReader);
112 -}
113 -
114 -/**
115 - * hex to rgb
116 - * @param {string} color - hex color
117 - * @param {string} alpha - color alpha value
118 - * @returns {string} rgb expression
119 - */
120 -export function getRgb(color, alpha) {
121 - if (color.length === 4) {
122 - color = `${color}${color.slice(1, 4)}`;
123 - }
124 - const r = parseInt(color.slice(1, 3), 16);
125 - const g = parseInt(color.slice(3, 5), 16);
126 - const b = parseInt(color.slice(5, 7), 16);
127 - const a = alpha || 1;
128 -
129 - return `rgba(${r}, ${g}, ${b}, ${a})`;
130 -}
131 -
132 -/**
133 - * send hostname
134 - */
135 -export function sendHostName() {
136 - if (hostnameSent) {
137 - return;
138 - }
139 - hostnameSent = true;
140 -
141 - sendHostname('image-editor', 'UA-129999381-1');
142 -}
143 -
144 -/**
145 - * Apply css resource
146 - * @param {string} styleBuffer - serialized css text
147 - * @param {string} tagId - style tag id
148 - */
149 -export function styleLoad(styleBuffer, tagId) {
150 - const [head] = document.getElementsByTagName('head');
151 - const linkElement = document.createElement('link');
152 - const styleData = encodeURIComponent(styleBuffer);
153 - if (tagId) {
154 - linkElement.id = tagId;
155 - // linkElement.id = 'tui-image-editor-theme-style';
156 - }
157 - linkElement.setAttribute('rel', 'stylesheet');
158 - linkElement.setAttribute('type', 'text/css');
159 - linkElement.setAttribute('href', `data:text/css;charset=UTF-8,${styleData}`);
160 - head.appendChild(linkElement);
161 -}
162 -
163 -/**
164 - * Get selector
165 - * @param {HTMLElement} targetElement - target element
166 - * @returns {Function} selector
167 - */
168 -export function getSelector(targetElement) {
169 - return (str) => targetElement.querySelector(str);
170 -}
171 -
172 -/**
173 - * Change base64 to blob
174 - * @param {String} data - base64 string data
175 - * @returns {Blob} Blob Data
176 - */
177 -export function base64ToBlob(data) {
178 - const rImageType = /data:(image\/.+);base64,/;
179 - let mimeString = '';
180 - let raw, uInt8Array, i;
181 -
182 - raw = data.replace(rImageType, (header, imageType) => {
183 - mimeString = imageType;
184 -
185 - return '';
186 - });
187 -
188 - raw = atob(raw);
189 - const rawLength = raw.length;
190 - uInt8Array = new Uint8Array(rawLength); // eslint-disable-line
191 -
192 - for (i = 0; i < rawLength; i += 1) {
193 - uInt8Array[i] = raw.charCodeAt(i);
194 - }
195 -
196 - return new Blob([uInt8Array], { type: mimeString });
197 -}
198 -
199 -/**
200 - * Fix floating point diff.
201 - * @param {number} value - original value
202 - * @returns {number} fixed value
203 - */
204 -export function fixFloatingPoint(value) {
205 - return Number(value.toFixed(FLOATING_POINT_DIGIT));
206 -}
207 -
208 -/**
209 - * Assignment for destroying objects.
210 - * @param {Object} targetObject - object to be removed.
211 - */
212 -export function assignmentForDestroy(targetObject) {
213 - forEach(targetObject, (value, key) => {
214 - targetObject[key] = null;
215 - });
216 -}
217 -
218 -/**
219 - * Make class name for ui
220 - * @param {String} str - main string of className
221 - * @param {String} prefix - prefix string of className
222 - * @returns {String} class name
223 - */
224 -export function cls(str = '', prefix = '') {
225 - if (str.charAt(0) === '.') {
226 - return `.${CSS_PREFIX}${prefix}${str.slice(1)}`;
227 - }
228 -
229 - return `${CSS_PREFIX}${prefix}${str}`;
230 -}
231 -
232 -/**
233 - * Change object origin
234 - * @param {fabric.Object} fObject - fabric object
235 - * @param {Object} origin - origin of fabric object
236 - * @param {string} originX - horizontal basis.
237 - * @param {string} originY - vertical basis.
238 - */
239 -export function changeOrigin(fObject, origin) {
240 - const { originX, originY } = origin;
241 - const { x: left, y: top } = fObject.getPointByOrigin(originX, originY);
242 -
243 - fObject.set({
244 - left,
245 - top,
246 - originX,
247 - originY,
248 - });
249 -
250 - fObject.setCoords();
251 -}
252 -
253 -/**
254 - * Object key value flip
255 - * @param {Object} targetObject - The data object of the key value.
256 - * @returns {Object}
257 - */
258 -export function flipObject(targetObject) {
259 - const result = {};
260 -
261 - Object.keys(targetObject).forEach((key) => {
262 - result[targetObject[key]] = key;
263 - });
264 -
265 - return result;
266 -}
267 -
268 -/**
269 - * Set custom properties
270 - * @param {Object} targetObject - target object
271 - * @param {Object} props - custom props object
272 - */
273 -export function setCustomProperty(targetObject, props) {
274 - targetObject.customProps = targetObject.customProps || {};
275 - extend(targetObject.customProps, props);
276 -}
277 -
278 -/**
279 - * Get custom property
280 - * @param {fabric.Object} fObject - fabric object
281 - * @param {Array|string} propNames - prop name array
282 - * @returns {object | number | string}
283 - */
284 -export function getCustomProperty(fObject, propNames) {
285 - const resultObject = {};
286 - if (isString(propNames)) {
287 - propNames = [propNames];
288 - }
289 - forEach(propNames, (propName) => {
290 - resultObject[propName] = fObject.customProps[propName];
291 - });
292 -
293 - return resultObject;
294 -}
295 -
296 -/**
297 - * Capitalize string
298 - * @param {string} targetString - target string
299 - * @returns {string}
300 - */
301 -export function capitalizeString(targetString) {
302 - return targetString.charAt(0).toUpperCase() + targetString.slice(1);
303 -}
304 -
305 -/**
306 - * Array includes check
307 - * @param {Array} targetArray - target array
308 - * @param {string|number} compareValue - compare value
309 - * @returns {boolean}
310 - */
311 -export function includes(targetArray, compareValue) {
312 - return targetArray.indexOf(compareValue) >= 0;
313 -}
314 -
315 -/**
316 - * Get fill type
317 - * @param {Object | string} fillOption - shape fill option
318 - * @returns {string} 'color' or 'filter'
319 - */
320 -export function getFillTypeFromOption(fillOption = {}) {
321 - return pick(fillOption, 'type') || SHAPE_FILL_TYPE.COLOR;
322 -}
323 -
324 -/**
325 - * Get fill type of shape type object
326 - * @param {fabric.Object} shapeObj - fabric object
327 - * @returns {string} 'transparent' or 'color' or 'filter'
328 - */
329 -export function getFillTypeFromObject(shapeObj) {
330 - const { fill = {} } = shapeObj;
331 - if (fill.source) {
332 - return SHAPE_FILL_TYPE.FILTER;
333 - }
334 -
335 - return SHAPE_FILL_TYPE.COLOR;
336 -}
337 -
338 -/**
339 - * Check if the object is a shape object.
340 - * @param {fabric.Object} obj - fabric object
341 - * @returns {boolean}
342 - */
343 -export function isShape(obj) {
344 - return inArray(obj.get('type'), SHAPE_TYPE) >= 0;
345 -}
1 -<?xml version="1.0" encoding="UTF-8"?>
2 -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3 -<svg display="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
4 -<defs id="tui-image-editor-svg-default-icons">
5 -<symbol id="ic-apply" viewBox="0 0 24 24">
6 - <path d="M0 0h24v24H0z" stroke="none" fill="none"/>
7 - <path fill="none" stroke="inherit" d="M4 12.011l5 5L20.011 6"/>
8 -</symbol>
9 -<symbol id="ic-cancel" viewBox="0 0 24 24">
10 - <path d="M0 0h24v24H0z" fill="none" stroke="none"/>
11 - <path fill="none" stroke="inherit" d="M6 6l12 12M18 6L6 18"/>
12 -</symbol>
13 -<symbol id="ic-crop" viewBox="0 0 24 24">
14 - <path d="M0 0h24v24H0z" stroke="none" fill="none" />
15 - <path stroke="none" fill="inherit" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
16 - <path stroke="none" fill="inherit" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
17 -</symbol>
18 -<symbol id="ic-delete-all" viewBox="0 0 24 24">
19 - <path stroke="none" fill="inherit" d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
20 - <path stroke="none" fill="inherit" d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
21 -</symbol>
22 -<symbol id="ic-delete" viewBox="0 0 24 24">
23 - <path stroke="none" fill="inherit" d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
24 - <path stroke="none" fill="inherit" d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
25 -</symbol>
26 -<symbol id="ic-draw-free" viewBox="0 0 32 32">
27 - <path fill="none" stroke="inherit" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
28 -</symbol>
29 -<symbol id="ic-draw-line" viewBox="0 0 32 32">
30 - <path fill="none" stroke="inherit" d="M2 15.5h28"/>
31 -</symbol>
32 -<symbol id="ic-draw" viewBox="0 0 24 24">
33 - <path fill="none" stroke="inherit" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
34 - <path stroke="none" fill="inherit" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
35 -</symbol>
36 -<symbol id="ic-filter" viewBox="0 0 24 24">
37 - <path d="M0 0h24v24H0z" fill="none" stroke="none" />
38 - <path stroke="none" fill="inherit" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
39 - <path stroke="none" fill="inherit" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
40 -</symbol>
41 -<symbol id="ic-flip-reset" viewBox="0 0 31 32">
42 - <path fill="none" stroke="none" d="M31 0H0v32h31z"/>
43 - <path stroke="none" fill="inherit" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
44 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
45 -</symbol>
46 -<symbol id="ic-flip-x" viewBox="0 0 32 32">
47 - <path fill="none" stroke="none" d="M32 32H0V0h32z"/>
48 - <path stroke="none" fill="inherit" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
49 -</symbol>
50 -<symbol id="ic-flip-y" viewBox="0 0 32 32">
51 - <path fill="none" stroke="none" d="M0 0v32h32V0z"/>
52 - <path stroke="none" fill="inherit" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
53 -</symbol>
54 -<symbol id="ic-flip" viewBox="0 0 24 24">
55 - <path d="M0 0h24v24H0z" fill="none" stroke="none" />
56 - <path fill="inherit" stroke="none" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
57 -</symbol>
58 -<symbol id="ic-icon-arrow-2" viewBox="0 0 32 32">
59 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
60 -</symbol>
61 -<symbol id="ic-icon-arrow-3" viewBox="0 0 32 32">
62 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
63 -</symbol>
64 -<symbol id="ic-icon-arrow" viewBox="0 0 32 32">
65 - <path fill="none" stroke="inherit" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
66 -</symbol>
67 -<symbol id="ic-icon-bubble" viewBox="0 0 32 32">
68 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
69 -</symbol>
70 -<symbol id="ic-icon-heart" viewBox="0 0 32 32">
71 - <path fill-rule="nonzero" fill="none" stroke="inherit" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
72 -</symbol>
73 -<symbol id="ic-icon-load" viewBox="0 0 32 32">
74 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
75 - <path stroke="none" fill="inherit" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
76 - <path stroke="none" fill="inherit" d="M25 3h1v9h-1z"/>
77 - <path fill="none" stroke="inherit" d="M22 6l3.5-3.5L29 6"/>
78 -</symbol>
79 -<symbol id="ic-icon-location" viewBox="0 0 32 32">
80 - <path fill="none" stroke="inherit" d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
81 - <circle fill="none" stroke="inherit" cx="16" cy="13" r="4.5"/>
82 -</symbol>
83 -<symbol id="ic-icon-polygon" viewBox="0 0 32 32">
84 - <path fill="none" stroke="inherit" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
85 -</symbol>
86 -<symbol id="ic-icon-star-2" viewBox="0 0 32 32">
87 - <path fill="none" stroke="inherit" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
88 -</symbol>
89 -<symbol id="ic-icon-star" viewBox="0 0 32 32">
90 - <path fill="none" stroke="inherit" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
91 -</symbol>
92 -<symbol id="ic-icon" viewBox="0 0 24 24">
93 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
94 -</symbol>
95 -<symbol id="ic-mask-load" viewBox="0 0 32 32">
96 - <path stroke="none" fill="none" d="M0 0h32v32H0z"/>
97 - <path stroke="none" fill="inherit" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
98 - <path stroke="none" fill="inherit" d="M25 3h1v9h-1z"/>
99 - <path fill="none" stroke="inherit" d="M22 6l3.5-3.5L29 6"/>
100 -</symbol>
101 -<symbol id="ic-mask" viewBox="0 0 24 24">
102 - <circle cx="12" cy="12" r="4.5" stroke="inherit" fill="none"/>
103 - <path stroke="none" fill="inherit" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
104 -</symbol>
105 -<symbol id="ic-redo" viewBox="0 0 24 24">
106 - <path d="M0 0h24v24H0z" opacity=".5" fill="none" stroke="none" />
107 - <path stroke="none" fill="inherit" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
108 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
109 -</symbol>
110 -<symbol id="ic-reset" viewBox="0 0 24 24">
111 - <path d="M0 0h24v24H0z" opacity=".5" stroke="none" fill="none"/>
112 - <path stroke="none" fill="inherit" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
113 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
114 -</symbol>
115 -<symbol id="ic-rotate-clockwise" viewBox="0 0 32 32">
116 - <path stroke="none" fill="inherit" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
117 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
118 - <path stroke="none" fill="inherit" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
119 -</symbol>
120 -<symbol id="ic-rotate-counterclockwise" viewBox="0 0 32 32">
121 - <path stroke="none" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
122 - <path stroke="none" fill="inherit" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
123 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
124 -</symbol>
125 -<symbol id="ic-rotate" viewBox="0 0 24 24">
126 - <path d="M0 0h24v24H0z" fill="none" stroke="none" />
127 - <path fill="inherit" stroke="none" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
128 - <path stroke="inherit" fill="none" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
129 -</symbol>
130 -<symbol id="ic-shape-circle" viewBox="0 0 32 32">
131 - <circle cx="16" cy="16" r="14.5" fill="none" stroke="inherit"/>
132 -</symbol>
133 -<symbol id="ic-shape-rectangle" viewBox="0 0 32 32">
134 - <rect width="27" height="27" x="2.5" y="2.5" fill="none" stroke="inherit" rx="1"/>
135 -</symbol>
136 -<symbol id="ic-shape-triangle" viewBox="0 0 32 32">
137 - <path fill="none" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
138 -</symbol>
139 -<symbol id="ic-shape" viewBox="0 0 24 24">
140 - <path stroke="none" fill="inherit" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
141 - <path fill="none" stroke="inherit" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
142 -</symbol>
143 -<symbol id="ic-text-align-center" viewBox="0 0 32 32">
144 - <path stroke="none" fill="none" d="M0 0h32v32H0z"/>
145 - <path stroke="none" fill="inherit" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
146 -</symbol>
147 -<symbol id="ic-text-align-left" viewBox="0 0 32 32">
148 - <path stroke="none" fill="none" d="M0 0h32v32H0z"/>
149 - <path stroke="none" fill="inherit" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
150 -</symbol>
151 -<symbol id="ic-text-align-right" viewBox="0 0 32 32">
152 - <path stroke="none" fill="none" d="M0 0h32v32H0z"/>
153 - <path stroke="none" fill="inherit" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
154 -</symbol>
155 -<symbol id="ic-text-bold" viewBox="0 0 32 32">
156 - <path fill="none" stroke="none" d="M0 0h32v32H0z"/>
157 - <path stroke="none" fill="inherit" d="M7 2h2v2H7zM7 28h2v2H7z"/>
158 - <path fill="none" stroke="inherit" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
159 -</symbol>
160 -<symbol id="ic-text-italic" viewBox="0 0 32 32">
161 - <path fill="none" stroke="none" d="M0 0h32v32H0z"/>
162 - <path stroke="none" fill="inherit" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
163 -</symbol>
164 -<symbol id="ic-text-underline" viewBox="0 0 32 32">
165 - <path stroke="none" fill="none" d="M0 0h32v32H0z"/>
166 - <path stroke="none" fill="inherit" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
167 - <path stroke="none" fill="inherit" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
168 -</symbol>
169 -<symbol id="ic-text" viewBox="0 0 24 24">
170 - <path stroke="none" fill="inherit" d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
171 - <path stroke="none" fill="inherit" d="M11 3h1v18h-1z"/>
172 - <path stroke="none" fill="inherit" d="M10 20h3v1h-3z"/>
173 -</symbol>
174 -<symbol id="ic-undo" viewBox="0 0 24 24">
175 - <path d="M24 0H0v24h24z" opacity=".5" fill="none" stroke="none" />
176 - <path stroke="none" fill="inherit" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
177 - <path fill="none" stroke="inherit" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
178 -</symbol>
179 -</defs>
180 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#434343" d="M4 12.011l5 5L20.011 6"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#434343" d="M6 6l12 12M18 6L6 18"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32">
2 - <defs>
3 - <circle id="a" cx="16" cy="16" r="16"/>
4 - </defs>
5 - <g fill="none" fill-rule="evenodd">
6 - <g>
7 - <use fill="#FFF" xlink:href="#a"/>
8 - <circle cx="16" cy="16" r="15.5" stroke="#D5D5D5"/>
9 - </g>
10 - <path stroke="#FF4040" stroke-width="1.5" d="M27 5L5 27"/>
11 - </g>
12 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#434343" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
5 - <path fill="#434343" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#434343" fill-rule="evenodd">
3 - <path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#434343" fill-rule="evenodd">
3 - <path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M2 15.5h28"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#434343" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
4 - <path fill="#434343" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#434343" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
5 - <path fill="#434343" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="31" height="32" viewBox="0 0 31 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M31 0H0v32h31z"/>
4 - <path fill="#434343" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M32 32H0V0h32z"/>
4 - <path fill="#434343" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0v32h32V0z"/>
4 - <path fill="#434343" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#434343" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill-rule="nonzero" stroke="#434343" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
4 - <path fill="#434343" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
5 - <path fill="#434343" d="M25 3h1v9h-1z"/>
6 - <path stroke="#434343" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <g stroke="#434343">
4 - <path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
5 - <circle cx="16" cy="13" r="4.5"/>
6 - </g>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
5 - <path fill="#434343" d="M25 3h1v9h-1z"/>
6 - <path stroke="#434343" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <circle cx="12" cy="12" r="4.5" stroke="#434343"/>
4 - <path fill="#434343" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#434343" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#434343" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#434343" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
4 - <path stroke="#434343" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
5 - <path fill="#434343" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#434343" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
4 - <path fill="#434343" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#434343" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <circle cx="16" cy="16" r="14.5" stroke="#434343"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <rect width="27" height="27" x="2.5" y="2.5" stroke="#434343" rx="1"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#434343" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
4 - <path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M7 2h2v2H7zM7 28h2v2H7z"/>
5 - <path stroke="#434343" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#434343" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
5 - <path fill="#434343" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#434343" fill-rule="evenodd">
3 - <path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
4 - <path d="M11 3h1v18h-1z"/>
5 - <path d="M10 20h3v1h-3z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M24 0H0v24h24z" opacity=".5"/>
4 - <path fill="#434343" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
5 - <path stroke="#434343" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="257" height="26" viewBox="0 0 257 26">
2 - <g fill="#FDBA3B">
3 - <path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#555555" d="M4 12.011l5 5L20.011 6"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#555555" d="M6 6l12 12M18 6L6 18"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#555555" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
5 - <path fill="#555555" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#555555" fill-rule="evenodd">
3 - <path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#555555" fill-rule="evenodd">
3 - <path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M2 15.5h28"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#555555" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
4 - <path fill="#555555" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#555555" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
5 - <path fill="#555555" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="31" height="32" viewBox="0 0 31 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M31 0H0v32h31z"/>
4 - <path fill="#555555" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M32 32H0V0h32z"/>
4 - <path fill="#555555" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0v32h32V0z"/>
4 - <path fill="#555555" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#555555" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill-rule="nonzero" stroke="#555555" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
4 - <path fill="#555555" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
5 - <path fill="#555555" d="M25 3h1v9h-1z"/>
6 - <path stroke="#555555" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <g stroke="#555555">
4 - <path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
5 - <circle cx="16" cy="13" r="4.5"/>
6 - </g>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
5 - <path fill="#555555" d="M25 3h1v9h-1z"/>
6 - <path stroke="#555555" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <circle cx="12" cy="12" r="4.5" stroke="#555555"/>
4 - <path fill="#555555" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#555555" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#555555" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#555555" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
4 - <path stroke="#555555" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
5 - <path fill="#555555" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#555555" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
4 - <path fill="#555555" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#555555" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <circle cx="16" cy="16" r="14.5" stroke="#555555"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <rect width="27" height="27" x="2.5" y="2.5" stroke="#555555" rx="1"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#555555" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
4 - <path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M7 2h2v2H7zM7 28h2v2H7z"/>
5 - <path stroke="#555555" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#555555" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
5 - <path fill="#555555" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#555555" fill-rule="evenodd">
3 - <path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
4 - <path d="M11 3h1v18h-1z"/>
5 - <path d="M10 20h3v1h-3z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M24 0H0v24h24z" opacity=".5"/>
4 - <path fill="#555555" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
5 - <path stroke="#555555" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="257" height="26" viewBox="0 0 257 26">
2 - <g fill="#FDBA3B">
3 - <path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#e9e9e9" d="M4 12.011l5 5L20.011 6"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#e9e9e9" d="M6 6l12 12M18 6L6 18"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#e9e9e9" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
5 - <path fill="#e9e9e9" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#e9e9e9" fill-rule="evenodd">
3 - <path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#e9e9e9" fill-rule="evenodd">
3 - <path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M2 15.5h28"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#e9e9e9" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
4 - <path fill="#e9e9e9" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#e9e9e9" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
5 - <path fill="#e9e9e9" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="31" height="32" viewBox="0 0 31 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M31 0H0v32h31z"/>
4 - <path fill="#e9e9e9" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M32 32H0V0h32z"/>
4 - <path fill="#e9e9e9" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0v32h32V0z"/>
4 - <path fill="#e9e9e9" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#e9e9e9" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill-rule="nonzero" stroke="#e9e9e9" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
4 - <path fill="#e9e9e9" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
5 - <path fill="#e9e9e9" d="M25 3h1v9h-1z"/>
6 - <path stroke="#e9e9e9" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <g stroke="#e9e9e9">
4 - <path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
5 - <circle cx="16" cy="13" r="4.5"/>
6 - </g>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
5 - <path fill="#e9e9e9" d="M25 3h1v9h-1z"/>
6 - <path stroke="#e9e9e9" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <circle cx="12" cy="12" r="4.5" stroke="#e9e9e9"/>
4 - <path fill="#e9e9e9" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#e9e9e9" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#e9e9e9" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#e9e9e9" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
4 - <path stroke="#e9e9e9" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
5 - <path fill="#e9e9e9" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#e9e9e9" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
4 - <path fill="#e9e9e9" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#e9e9e9" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <circle cx="16" cy="16" r="14.5" stroke="#e9e9e9"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <rect width="27" height="27" x="2.5" y="2.5" stroke="#e9e9e9" rx="1"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#e9e9e9" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
4 - <path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M7 2h2v2H7zM7 28h2v2H7z"/>
5 - <path stroke="#e9e9e9" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#e9e9e9" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
5 - <path fill="#e9e9e9" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#e9e9e9" fill-rule="evenodd">
3 - <path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
4 - <path d="M11 3h1v18h-1z"/>
5 - <path d="M10 20h3v1h-3z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M24 0H0v24h24z" opacity=".5"/>
4 - <path fill="#e9e9e9" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
5 - <path stroke="#e9e9e9" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="257" height="26" viewBox="0 0 257 26">
2 - <g fill="#FDBA3B">
3 - <path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#8a8a8a" d="M4 12.011l5 5L20.011 6"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path stroke="#8a8a8a" d="M6 6l12 12M18 6L6 18"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#8a8a8a" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
5 - <path fill="#8a8a8a" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#8a8a8a" fill-rule="evenodd">
3 - <path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#8a8a8a" fill-rule="evenodd">
3 - <path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
4 - <path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M2 15.5h28"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#8a8a8a" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
4 - <path fill="#8a8a8a" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#8a8a8a" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
5 - <path fill="#8a8a8a" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="31" height="32" viewBox="0 0 31 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M31 0H0v32h31z"/>
4 - <path fill="#8a8a8a" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M32 32H0V0h32z"/>
4 - <path fill="#8a8a8a" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0v32h32V0z"/>
4 - <path fill="#8a8a8a" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#8a8a8a" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill-rule="nonzero" stroke="#8a8a8a" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
4 - <path fill="#8a8a8a" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
5 - <path fill="#8a8a8a" d="M25 3h1v9h-1z"/>
6 - <path stroke="#8a8a8a" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <g stroke="#8a8a8a">
4 - <path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
5 - <circle cx="16" cy="13" r="4.5"/>
6 - </g>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
5 - <path fill="#8a8a8a" d="M25 3h1v9h-1z"/>
6 - <path stroke="#8a8a8a" d="M22 6l3.5-3.5L29 6"/>
7 - </g>
8 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none">
3 - <circle cx="12" cy="12" r="4.5" stroke="#8a8a8a"/>
4 - <path fill="#8a8a8a" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#8a8a8a" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z" opacity=".5"/>
4 - <path fill="#8a8a8a" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#8a8a8a" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
4 - <path stroke="#8a8a8a" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
5 - <path fill="#8a8a8a" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#8a8a8a" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
4 - <path fill="#8a8a8a" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h24v24H0z"/>
4 - <path fill="#8a8a8a" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <circle cx="16" cy="16" r="14.5" stroke="#8a8a8a"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <rect width="27" height="27" x="2.5" y="2.5" stroke="#8a8a8a" rx="1"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
4 - </g>
5 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path fill="#8a8a8a" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
4 - <path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M7 2h2v2H7zM7 28h2v2H7z"/>
5 - <path stroke="#8a8a8a" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
5 - </g>
6 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M0 0h32v32H0z"/>
4 - <path fill="#8a8a8a" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
5 - <path fill="#8a8a8a" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="#8a8a8a" fill-rule="evenodd">
3 - <path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
4 - <path d="M11 3h1v18h-1z"/>
5 - <path d="M10 20h3v1h-3z"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2 - <g fill="none" fill-rule="evenodd">
3 - <path d="M24 0H0v24h24z" opacity=".5"/>
4 - <path fill="#8a8a8a" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
5 - <path stroke="#8a8a8a" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
6 - </g>
7 -</svg>
1 -<svg xmlns="http://www.w3.org/2000/svg" width="257" height="26" viewBox="0 0 257 26">
2 - <g fill="#FDBA3B">
3 - <path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
4 - </g>
5 -</svg>