@qtoggle/qui 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.json +492 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  5. package/.github/ISSUE_TEMPLATE/improvement_proposal.md +20 -0
  6. package/.github/workflows/main.yml +74 -0
  7. package/.pre-commit-config.yaml +8 -0
  8. package/LICENSE.txt +177 -0
  9. package/README.md +4 -0
  10. package/font/dejavusans-bold.woff +0 -0
  11. package/font/dejavusans-bolditalic.woff +0 -0
  12. package/font/dejavusans-italic.woff +0 -0
  13. package/font/dejavusans-regular.woff +0 -0
  14. package/img/qui-icons.svg +1937 -0
  15. package/js/base/base.js +47 -0
  16. package/js/base/condition-variable.js +92 -0
  17. package/js/base/errors.js +36 -0
  18. package/js/base/i18n.js +20 -0
  19. package/js/base/mixwith.js +135 -0
  20. package/js/base/require-js-compat.js +78 -0
  21. package/js/base/signal.js +91 -0
  22. package/js/base/singleton.js +66 -0
  23. package/js/base/timer.js +126 -0
  24. package/js/config.js +184 -0
  25. package/js/forms/common-fields/check-field.js +42 -0
  26. package/js/forms/common-fields/choice-buttons-field.js +30 -0
  27. package/js/forms/common-fields/color-combo-field.js +37 -0
  28. package/js/forms/common-fields/combo-field.js +108 -0
  29. package/js/forms/common-fields/common-fields.js +23 -0
  30. package/js/forms/common-fields/composite-field.js +132 -0
  31. package/js/forms/common-fields/custom-html-field.js +51 -0
  32. package/js/forms/common-fields/email-field.js +30 -0
  33. package/js/forms/common-fields/file-picker-field.js +46 -0
  34. package/js/forms/common-fields/jquery-ui-field.js +111 -0
  35. package/js/forms/common-fields/labels-field.js +69 -0
  36. package/js/forms/common-fields/numeric-field.js +39 -0
  37. package/js/forms/common-fields/password-field.js +28 -0
  38. package/js/forms/common-fields/phone-field.js +26 -0
  39. package/js/forms/common-fields/progress-disk-field.js +69 -0
  40. package/js/forms/common-fields/push-button-field.js +138 -0
  41. package/js/forms/common-fields/slider-field.js +51 -0
  42. package/js/forms/common-fields/text-area-field.js +34 -0
  43. package/js/forms/common-fields/text-field.js +89 -0
  44. package/js/forms/common-fields/up-down-field.js +85 -0
  45. package/js/forms/common-forms/common-forms.js +16 -0
  46. package/js/forms/common-forms/options-form.js +77 -0
  47. package/js/forms/common-forms/page-form.js +115 -0
  48. package/js/forms/form-button.js +202 -0
  49. package/js/forms/form-field.js +1183 -0
  50. package/js/forms/form.js +1181 -0
  51. package/js/forms/forms.js +68 -0
  52. package/js/global-glass.js +100 -0
  53. package/js/icons/default-stock.js +173 -0
  54. package/js/icons/icon.js +64 -0
  55. package/js/icons/icons.js +16 -0
  56. package/js/icons/multi-state-sprites-icon.js +362 -0
  57. package/js/icons/stock-icon.js +219 -0
  58. package/js/icons/stock.js +98 -0
  59. package/js/icons/stocks.js +57 -0
  60. package/js/index.js +232 -0
  61. package/js/lib/jquery.longpress.js +79 -0
  62. package/js/lib/jquery.module.js +4 -0
  63. package/js/lib/logger.module.js +4 -0
  64. package/js/lib/pep.module.js +4 -0
  65. package/js/lists/common-items/common-items.js +5 -0
  66. package/js/lists/common-items/icon-label-list-item.js +86 -0
  67. package/js/lists/common-lists/common-lists.js +5 -0
  68. package/js/lists/common-lists/page-list.js +53 -0
  69. package/js/lists/list-item.js +147 -0
  70. package/js/lists/list.js +636 -0
  71. package/js/lists/lists.js +26 -0
  72. package/js/main-ui/main-ui.js +64 -0
  73. package/js/main-ui/menu-bar.js +144 -0
  74. package/js/main-ui/options-bar.js +181 -0
  75. package/js/main-ui/status.js +185 -0
  76. package/js/main-ui/top-bar.js +59 -0
  77. package/js/messages/common-message-forms/common-message-forms.js +7 -0
  78. package/js/messages/common-message-forms/confirm-message-form.js +81 -0
  79. package/js/messages/common-message-forms/simple-message-form.js +67 -0
  80. package/js/messages/common-message-forms/sticky-simple-message-form.js +27 -0
  81. package/js/messages/message-form.js +107 -0
  82. package/js/messages/messages.js +21 -0
  83. package/js/messages/sticky-modal-page.js +98 -0
  84. package/js/messages/sticky-modal-progress-message.js +27 -0
  85. package/js/messages/toast.js +164 -0
  86. package/js/navigation.js +654 -0
  87. package/js/pages/breadcrumbs.js +124 -0
  88. package/js/pages/common-pages/common-pages.js +6 -0
  89. package/js/pages/common-pages/modal-progress-page.js +83 -0
  90. package/js/pages/common-pages/structured-page.js +46 -0
  91. package/js/pages/page.js +1018 -0
  92. package/js/pages/pages-context.js +154 -0
  93. package/js/pages/pages.js +252 -0
  94. package/js/pwa.js +337 -0
  95. package/js/sections/section.js +612 -0
  96. package/js/sections/sections.js +300 -0
  97. package/js/tables/common-cells/common-cells.js +7 -0
  98. package/js/tables/common-cells/icon-label-table-cell.js +68 -0
  99. package/js/tables/common-cells/push-button-table-cell.js +133 -0
  100. package/js/tables/common-cells/simple-table-cell.js +37 -0
  101. package/js/tables/common-tables/common-tables.js +5 -0
  102. package/js/tables/common-tables/page-table.js +55 -0
  103. package/js/tables/table-cell.js +198 -0
  104. package/js/tables/table-row.js +126 -0
  105. package/js/tables/table.js +492 -0
  106. package/js/tables/tables.js +36 -0
  107. package/js/theme.js +304 -0
  108. package/js/utils/ajax.js +126 -0
  109. package/js/utils/array.js +194 -0
  110. package/js/utils/colors.js +445 -0
  111. package/js/utils/cookies.js +65 -0
  112. package/js/utils/crypto.js +439 -0
  113. package/js/utils/css.js +234 -0
  114. package/js/utils/date.js +300 -0
  115. package/js/utils/files.js +27 -0
  116. package/js/utils/gestures.js +165 -0
  117. package/js/utils/html.js +76 -0
  118. package/js/utils/misc.js +81 -0
  119. package/js/utils/object.js +324 -0
  120. package/js/utils/promise.js +49 -0
  121. package/js/utils/string.js +192 -0
  122. package/js/utils/url.js +187 -0
  123. package/js/utils/utils.js +3 -0
  124. package/js/utils/visibility-manager.js +211 -0
  125. package/js/views/common-views/common-views.js +7 -0
  126. package/js/views/common-views/icon-label-view.js +210 -0
  127. package/js/views/common-views/progress-view.js +89 -0
  128. package/js/views/common-views/structured-view.js +368 -0
  129. package/js/views/view.js +467 -0
  130. package/js/views/views.js +3 -0
  131. package/js/widgets/base-widget.js +23 -0
  132. package/js/widgets/common-widgets/check-button.js +109 -0
  133. package/js/widgets/common-widgets/choice-buttons.js +322 -0
  134. package/js/widgets/common-widgets/color-combo.js +104 -0
  135. package/js/widgets/common-widgets/combo.js +645 -0
  136. package/js/widgets/common-widgets/common-widgets.js +17 -0
  137. package/js/widgets/common-widgets/email-input.js +7 -0
  138. package/js/widgets/common-widgets/file-picker.js +133 -0
  139. package/js/widgets/common-widgets/labels.js +132 -0
  140. package/js/widgets/common-widgets/numeric-input.js +49 -0
  141. package/js/widgets/common-widgets/password-input.js +91 -0
  142. package/js/widgets/common-widgets/phone-input.js +7 -0
  143. package/js/widgets/common-widgets/progress-disk.js +174 -0
  144. package/js/widgets/common-widgets/push-button.js +155 -0
  145. package/js/widgets/common-widgets/slider.js +455 -0
  146. package/js/widgets/common-widgets/text-area.js +52 -0
  147. package/js/widgets/common-widgets/text-input.js +174 -0
  148. package/js/widgets/common-widgets/up-down.js +351 -0
  149. package/js/widgets/widgets.js +57 -0
  150. package/js/window.js +557 -0
  151. package/jsdoc.conf.json +20 -0
  152. package/less/base.less +123 -0
  153. package/less/forms/common-fields.less +101 -0
  154. package/less/forms/common-forms.less +5 -0
  155. package/less/forms/form-button.less +21 -0
  156. package/less/forms/form-field.less +266 -0
  157. package/less/forms/form.less +131 -0
  158. package/less/global-glass.less +64 -0
  159. package/less/icon-label-view.less +82 -0
  160. package/less/icons.less +144 -0
  161. package/less/lists.less +105 -0
  162. package/less/main-ui.less +328 -0
  163. package/less/messages.less +189 -0
  164. package/less/no-effects.less +24 -0
  165. package/less/pages/breadcrumbs.less +98 -0
  166. package/less/pages/common-pages.less +36 -0
  167. package/less/pages/page.less +70 -0
  168. package/less/progress-view.less +51 -0
  169. package/less/stock-icons.less +43 -0
  170. package/less/structured-view.less +245 -0
  171. package/less/tables.less +84 -0
  172. package/less/theme-dark.less +133 -0
  173. package/less/theme-light.less +132 -0
  174. package/less/theme.less +419 -0
  175. package/less/visibility-manager.less +11 -0
  176. package/less/widgets/check-button.less +96 -0
  177. package/less/widgets/choice-buttons.less +160 -0
  178. package/less/widgets/color-combo.less +33 -0
  179. package/less/widgets/combo.less +230 -0
  180. package/less/widgets/common-buttons.less +120 -0
  181. package/less/widgets/common.less +24 -0
  182. package/less/widgets/input.less +258 -0
  183. package/less/widgets/labels.less +81 -0
  184. package/less/widgets/progress-disk.less +70 -0
  185. package/less/widgets/slider.less +199 -0
  186. package/less/widgets/updown.less +115 -0
  187. package/less/widgets/various.less +36 -0
  188. package/package.json +47 -0
  189. package/pyproject.toml +45 -0
  190. package/qui/__init__.py +110 -0
  191. package/qui/constants.py +1 -0
  192. package/qui/exceptions.py +2 -0
  193. package/qui/j2template.py +71 -0
  194. package/qui/settings.py +60 -0
  195. package/qui/templates/manifest.json +25 -0
  196. package/qui/templates/qui.html +126 -0
  197. package/qui/templates/service-worker.js +188 -0
  198. package/qui/web/__init__.py +0 -0
  199. package/qui/web/tornado.py +220 -0
  200. package/scripts/postinstall.sh +10 -0
  201. package/webpack/webpack-adjust-css-urls-loader.js +36 -0
  202. package/webpack/webpack-common.js +384 -0
@@ -0,0 +1,133 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import {gettext} from '$qui/base/i18n.js'
5
+ import StockIcon from '$qui/icons/stock-icon.js'
6
+ import * as StringUtils from '$qui/utils/string.js'
7
+
8
+ import * as BaseWidget from '../base-widget.js' /* Needed */
9
+
10
+
11
+ $.widget('qui.filepicker', $.qui.basewidget, {
12
+
13
+ options: {
14
+ name: '',
15
+ placeholder: null,
16
+ accept: null,
17
+ multiple: false,
18
+ readonly: false,
19
+ disabled: false
20
+ },
21
+
22
+ _create: function () {
23
+ this.element.addClass('qui-file-picker')
24
+
25
+ /* Actual file input element */
26
+ this._input = $('<input>', {type: 'file'})
27
+ this.element.append(this._input)
28
+ this._input.on('change', function () {
29
+ this._button.pushbutton({caption: this.prepareCaption()})
30
+ }.bind(this))
31
+
32
+ /* Button */
33
+ this._button = $('<div>', {class: 'qui-file-picker-button qui-push-button'}).pushbutton({
34
+ caption: this.prepareCaption(),
35
+ icon: new StockIcon({name: 'file'})
36
+ })
37
+ this.element.append(this._button)
38
+ this._button.on('click', function () {
39
+ if (!this.options.readonly) {
40
+ this._input.trigger('click')
41
+ }
42
+ }.bind(this))
43
+
44
+ if (this.options.name) {
45
+ this._input.attr('name', this.options.name)
46
+ }
47
+
48
+ if (this.options.accept) {
49
+ this._input.attr('accept', this.options.accept.join(','))
50
+ }
51
+ if (this.options.multiple) {
52
+ this._input.attr('multiple', 'multiple')
53
+ }
54
+ if (this.options.readonly) {
55
+ this._input.attr('readonly', 'readonly')
56
+ }
57
+ if (this.options.disabled) {
58
+ this._input.attr('disabled', 'disabled')
59
+ }
60
+
61
+ /* Manually propagate some events to parent */
62
+ this._input.on('focus blur', function (e) {
63
+ this.element.triggerHandler(e.type)
64
+ }.bind(this))
65
+ },
66
+
67
+ _setOption: function (key, value) {
68
+ this._super(key, value)
69
+
70
+ switch (key) {
71
+ case 'accept':
72
+ if (value) {
73
+ this._input.attr('accept', value.join(','))
74
+ }
75
+ else {
76
+ this._input.removeAttr('accept')
77
+ }
78
+ break
79
+
80
+ case 'multiple':
81
+ if (value) {
82
+ this._input.attr('multiple', 'multiple')
83
+ }
84
+ else {
85
+ this._input.removeAttr('multiple')
86
+ }
87
+ break
88
+
89
+ case 'disabled':
90
+ this._button.pushbutton({disabled: value})
91
+ break
92
+
93
+ case 'readonly':
94
+ this._button.pushbutton({readonly: value})
95
+ break
96
+
97
+ case 'placeholder':
98
+ this._button.pushbutton({caption: this.prepareCaption()})
99
+ break
100
+
101
+ case 'warning':
102
+ this._button.pushbutton({style: value != null ? 'highlight' : 'interactive'})
103
+ break
104
+
105
+ case 'error':
106
+ this._button.pushbutton({style: value != null ? 'danger' : 'interactive'})
107
+ break
108
+ }
109
+ },
110
+
111
+ getValue: function () {
112
+ let files = this._input[0].files
113
+ return Array.prototype.slice.call(files)
114
+ },
115
+
116
+ setValue: function (value) {
117
+ /* Does nothing */
118
+ },
119
+
120
+ prepareCaption: function () {
121
+ let files = this._input[0].files
122
+ if (files.length > 1) {
123
+ return StringUtils.formatPercent('%(count)d files selected', {count: files.length})
124
+ }
125
+ else if (files.length > 0) {
126
+ return files[0].name
127
+ }
128
+ else {
129
+ return this.options.placeholder || gettext('Choose...')
130
+ }
131
+ }
132
+
133
+ })
@@ -0,0 +1,132 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import * as Theme from '$qui/theme.js'
5
+
6
+ import * as BaseWidget from '../base-widget.js' /* Needed */
7
+
8
+
9
+ $.widget('qui.labels', $.qui.basewidget, {
10
+
11
+ options: {
12
+ color: '@foreground-color',
13
+ background: '@background-color',
14
+ disabled: false,
15
+ chevrons: false,
16
+ clickable: false,
17
+ onClick(label, index) {}
18
+ },
19
+
20
+ _create: function () {
21
+ this.element.addClass('qui-labels-container')
22
+ if (this.options.disabled) {
23
+ this.element.addClass('disabled')
24
+ }
25
+ if (this.options.chevrons) {
26
+ this.element.addClass('chevrons')
27
+ }
28
+ if (this.options.clickable) {
29
+ this.element.addClass('clickable')
30
+ }
31
+
32
+ this._labels = []
33
+ },
34
+
35
+ _makeLabel: function (labelInfo) {
36
+ let labelSpan = $('<span></span>', {class: 'qui-label'})
37
+
38
+ let defColor = Theme.getColor(this.options.color)
39
+ let defBackground = Theme.getColor(this.options.background)
40
+
41
+ let color = defColor
42
+ let background = defBackground
43
+
44
+ let text = labelInfo
45
+ if (typeof labelInfo !== 'string') {
46
+ text = labelInfo.text
47
+ if (labelInfo.color) {
48
+ color = labelInfo.color
49
+ labelSpan.data('custom-color', true)
50
+ }
51
+ if (labelInfo.background) {
52
+ background = labelInfo.background
53
+ labelSpan.data('custom-background', true)
54
+ }
55
+ }
56
+
57
+ labelSpan.html(text)
58
+ labelSpan.css({
59
+ 'color': color,
60
+ 'background': background,
61
+ 'border-color': background
62
+ })
63
+
64
+ if (this.options.clickable) {
65
+ labelSpan.addClass('qui-base-button')
66
+ }
67
+
68
+ return labelSpan
69
+ },
70
+
71
+ getValue: function () {
72
+ return this._labels.map(l => l.html())
73
+ },
74
+
75
+ setValue: function (labels) {
76
+ this.element.html('')
77
+ this._labels = []
78
+
79
+ labels.forEach(function (labelInfo, index) {
80
+
81
+ let label = this._makeLabel(labelInfo)
82
+ this._labels.push(label)
83
+ this.element.append(label)
84
+
85
+ if (this.options.clickable) {
86
+ label.on('click', function () {
87
+ this.options.onClick(label.text(), index)
88
+ }.bind(this))
89
+ }
90
+
91
+ }, this)
92
+ },
93
+
94
+ _setOption: function (key, value) {
95
+ this._super(key, value)
96
+
97
+ switch (key) {
98
+ case 'color':
99
+ this._labels.forEach(function (label) {
100
+ if (!label.data('custom-background')) {
101
+ label.css('color', value)
102
+ }
103
+ })
104
+ break
105
+
106
+ case 'background':
107
+ this._labels.forEach(function (label) {
108
+ if (!label.data('custom-background')) {
109
+ label.css({
110
+ 'background': value,
111
+ 'border-color': value
112
+ })
113
+ }
114
+ })
115
+ break
116
+
117
+ case 'disabled':
118
+ this.element.toggleClass('disabled', value)
119
+ break
120
+
121
+ case 'chevrons':
122
+ this.element.toggleClass('chevrons', value)
123
+ break
124
+
125
+ case 'clickable':
126
+ this.element.children('span.qui-label').toggleClass('qui-base-button', value)
127
+ this.element.toggleClass('clickable', value)
128
+ break
129
+ }
130
+ }
131
+
132
+ })
@@ -0,0 +1,49 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import {asap} from '$qui/utils/misc.js'
5
+
6
+ import * as TextInput from './text-input.js' /* Needed */
7
+
8
+
9
+ // TODO do not allow user input of non-numeric characters
10
+ $.widget('qui.numericinput', $.qui.textinput, {
11
+
12
+ options: {
13
+ min: null,
14
+ max: null
15
+ },
16
+
17
+ type: 'number',
18
+
19
+ _create: function () {
20
+ this._super('_create')
21
+
22
+ this._setOption('min', this.options.min)
23
+ this._setOption('max', this.options.max)
24
+
25
+ /* Trigger change event on widget element when scrolling, but only if focused */
26
+ this._input.on('mousewheel', function (e) {
27
+ if (!this._input.is(':focus')) {
28
+ e.preventDefault()
29
+ return
30
+ }
31
+
32
+ asap(function () {
33
+ this.element.trigger('change')
34
+ }.bind(this))
35
+ }.bind(this))
36
+ },
37
+
38
+ _setOption: function (key, value) {
39
+ this._super(key, value)
40
+
41
+ switch (key) {
42
+ case 'min':
43
+ case 'max':
44
+ this._input.attr(key, value != null ? value : '')
45
+ break
46
+ }
47
+ }
48
+
49
+ })
@@ -0,0 +1,91 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import StockIcon from '$qui/icons/stock-icon.js'
5
+
6
+ import * as TextInput from './text-input.js' /* Needed */
7
+
8
+
9
+ $.widget('qui.passwordinput', $.qui.textinput, {
10
+
11
+ options: {
12
+ clearEnabled: false,
13
+ revealOnFocus: false
14
+ },
15
+
16
+ type: 'password',
17
+ _autocomplete_on: 'current-password',
18
+ _autocomplete_off: 'new-password',
19
+
20
+ _create: function () {
21
+ this._super()
22
+
23
+ this._clearButton = null
24
+ if (this.options.clearEnabled) {
25
+ this._clearButton = this._makeClearButton()
26
+ this.element.addClass('clear-enabled')
27
+ this.element.append(this._clearButton)
28
+ }
29
+
30
+ this.element.on('focus', function () {
31
+
32
+ if (this.options.revealOnFocus) {
33
+ this._input.attr('type', 'text')
34
+ }
35
+
36
+ }.bind(this))
37
+
38
+ this.element.on('blur', function () {
39
+
40
+ if (this.options.revealOnFocus) {
41
+ this._input.attr('type', this.type)
42
+ }
43
+
44
+ }.bind(this))
45
+ },
46
+
47
+ _makeClearButton: function () {
48
+ let clearIcon = $('<div></div>', {class: 'qui-icon qui-password-input-clear-icon'})
49
+ let variant = 'interactive'
50
+ if (this.options.error != null) {
51
+ variant = 'error'
52
+ }
53
+ else if (this.options.warning != null) {
54
+ variant = 'warning'
55
+ }
56
+ new StockIcon({
57
+ name: 'close', variant: variant,
58
+ activeName: 'close', activeVariant: 'background',
59
+ scale: 0.5
60
+ }).applyTo(clearIcon)
61
+
62
+ let clearButton = $('<div></div>', {class: 'qui-base-button qui-password-input-clear-button'})
63
+ clearButton.append(clearIcon)
64
+
65
+ clearButton.on('click', function () {
66
+
67
+ this._input.val('')
68
+ this.element.trigger('change')
69
+
70
+ }.bind(this))
71
+
72
+ return clearButton
73
+ },
74
+
75
+ _setOption: function (key, value) {
76
+ this._super(key, value)
77
+
78
+ switch (key) {
79
+ case 'warning':
80
+ case 'error':
81
+ /* When warning or error change, we need to rebuild the clear button icon */
82
+ if (this._clearButton) {
83
+ let newClearButton = this._makeClearButton()
84
+ this._clearButton.replaceWith(newClearButton)
85
+ this._clearButton = newClearButton
86
+ }
87
+ break
88
+ }
89
+ }
90
+
91
+ })
@@ -0,0 +1,7 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import * as TextInput from './text-input.js' /* Needed */
5
+
6
+
7
+ $.widget('qui.phoneinput', $.qui.textinput, {type: 'tel'}) // TODO do not allow user input of non-phone characters
@@ -0,0 +1,174 @@
1
+
2
+ import $ from '$qui/lib/jquery.module.js'
3
+
4
+ import * as Theme from '$qui/theme.js'
5
+ import * as CSS from '$qui/utils/css.js'
6
+ import * as StringUtils from '$qui/utils/string.js'
7
+
8
+ import * as BaseWidget from '../base-widget.js' /* Needed */
9
+
10
+
11
+ $.widget('qui.progressdisk', $.qui.basewidget, {
12
+
13
+ options: {
14
+ radius: '1em',
15
+ caption: '%s%%',
16
+ color: '@interactive-color',
17
+ disabled: false
18
+ },
19
+
20
+ _create: function () {
21
+ let ns = 'http://www.w3.org/2000/svg'
22
+
23
+ /* Root svg element */
24
+ this._svg = document.createElementNS(ns, 'svg')
25
+
26
+ /* Ring */
27
+ this._ring = document.createElementNS(ns, 'ellipse')
28
+ this._ring.setAttribute('class', 'qui-progress-disk-ring')
29
+ this._svg.appendChild(this._ring)
30
+
31
+ /* Cursor */
32
+ this._cursor = document.createElementNS(ns, 'path')
33
+ this._cursor.setAttribute('class', 'qui-progress-disk-cursor')
34
+ this._svg.appendChild(this._cursor)
35
+
36
+ /* Label */
37
+ this._label = $('<div></div>', {class: 'qui-progress-disk-label'})
38
+
39
+ this._curVal = 0
40
+ this._setValue(this._curVal)
41
+
42
+ this.element.addClass('qui-progress-disk-container')
43
+
44
+ if (this.options.disabled) {
45
+ this.element.addClass('disabled')
46
+ }
47
+
48
+ this.element.append(this._svg)
49
+ this.element.append(this._label)
50
+ },
51
+
52
+ getValue: function () {
53
+ return this._curVal
54
+ },
55
+
56
+ setValue: function (value) {
57
+ this._setValue(this._validate(value))
58
+ },
59
+
60
+ _setValue: function (value) {
61
+ this._curVal = value
62
+
63
+ if (value != null && value >= 0) {
64
+ this._label.html(StringUtils.formatPercent(this.options.caption, value.toFixed(0)))
65
+ this.element.removeClass('no-value')
66
+ }
67
+ else {
68
+ this._curVal = -1
69
+ this._label.html('')
70
+ this.element.addClass('no-value')
71
+ }
72
+
73
+ this._updateSVG()
74
+ },
75
+
76
+ _validate: function (value) {
77
+ value = parseFloat(value)
78
+ if (isNaN(value)) {
79
+ return -1
80
+ }
81
+
82
+ value = Math.max(-1, value)
83
+ value = Math.min(100, value)
84
+
85
+ return value
86
+ },
87
+
88
+ _makeArcPath: function (x, y, startAngle, endAngle, radius) {
89
+ let aFlag = '0'
90
+ if (endAngle - startAngle >= 180) {
91
+ aFlag = '1'
92
+ }
93
+ if (endAngle >= 360) {
94
+ endAngle = 359.9999
95
+ }
96
+
97
+ let x1 = x + radius * Math.cos(Math.PI * startAngle / 180)
98
+ let y1 = y + radius * Math.sin(Math.PI * startAngle / 180)
99
+ let x2 = x + radius * Math.cos(Math.PI * endAngle / 180)
100
+ let y2 = y + radius * Math.sin(Math.PI * endAngle / 180)
101
+ return (`M ${x1} ${y1} A ${radius} ${radius} 0 ${aFlag} 1 ${x2} ${y2}`)
102
+ },
103
+
104
+ _updateSVG: function () {
105
+ let isSmall = parseFloat(this.options.radius) < 2
106
+ let thickness = parseFloat(this.options.radius) / 4
107
+
108
+ this._label.toggleClass('small', isSmall)
109
+
110
+ let width = CSS.mulValue(this.options.radius, 2)
111
+ let fWidth = parseFloat(width)
112
+ let oRadius = parseFloat(this.options.radius)
113
+ let iRadius = oRadius - thickness / 2
114
+ let color = Theme.getColor(this.options.color)
115
+
116
+ this.element.width(width)
117
+ this.element.height(width)
118
+ this.element.css('line-height', width)
119
+
120
+ this._svg.setAttribute('width', width)
121
+ this._svg.setAttribute('height', width)
122
+ this._svg.setAttribute('viewBox', `0 0 ${fWidth} ${fWidth}`)
123
+
124
+ this._ring.setAttribute('cx', oRadius.toString())
125
+ this._ring.setAttribute('cy', oRadius.toString())
126
+ this._ring.setAttribute('rx', iRadius.toString())
127
+ this._ring.setAttribute('ry', iRadius.toString())
128
+ this._ring.setAttribute('stroke-width', thickness.toString())
129
+ this._ring.setAttribute('stroke', color)
130
+
131
+ this._cursor.setAttribute('stroke-width', thickness.toString())
132
+ this._cursor.setAttribute('stroke', color)
133
+
134
+ if (this._curVal >= 0) {
135
+ this._updateSVGPercent(iRadius, oRadius)
136
+ }
137
+ else {
138
+ this._updateSVGLoader(iRadius, oRadius)
139
+ }
140
+ },
141
+
142
+ _updateSVGPercent: function (iRadius, oRadius) {
143
+ let angle = 360 * this._curVal / 100
144
+ let path = this._makeArcPath(oRadius, oRadius, 0, angle, iRadius)
145
+
146
+ this._cursor.setAttribute('d', path)
147
+ },
148
+
149
+ _updateSVGLoader: function (iRadius, oRadius) {
150
+ let path = this._makeArcPath(oRadius, oRadius, -90, 0, iRadius)
151
+
152
+ this._cursor.setAttribute('d', path)
153
+ },
154
+
155
+ _setOption: function (key, value) {
156
+ this._super(key, value)
157
+
158
+ switch (key) {
159
+ case 'radius':
160
+ case 'color':
161
+ this._updateSVG()
162
+ break
163
+
164
+ case 'disabled':
165
+ this.element.toggleClass('disabled', value)
166
+ break
167
+
168
+ case 'caption':
169
+ this._setValue(this._curVal)
170
+ break
171
+ }
172
+ }
173
+
174
+ })