decidim-assemblies 0.20.1 → 0.23.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/decidim/assemblies/assembly.svg +1 -3
  3. data/app/assets/javascripts/decidim/assemblies/orgchart.js.es6 +698 -0
  4. data/app/cells/decidim/assemblies/assembly_m/footer.erb +2 -2
  5. data/app/cells/decidim/assemblies/assembly_m_cell.rb +5 -5
  6. data/app/cells/decidim/assemblies/assembly_member/show.erb +4 -4
  7. data/app/cells/decidim/assemblies/content_blocks/highlighted_assemblies/show.erb +10 -11
  8. data/app/cells/decidim/assemblies/content_blocks/highlighted_assemblies_cell.rb +1 -0
  9. data/app/commands/decidim/assemblies/admin/create_assemblies_type.rb +45 -0
  10. data/app/commands/decidim/assemblies/admin/create_assembly.rb +1 -2
  11. data/app/commands/decidim/assemblies/admin/create_assembly_admin.rb +2 -1
  12. data/app/commands/decidim/assemblies/admin/create_assembly_member.rb +12 -3
  13. data/app/commands/decidim/assemblies/admin/destroy_assemblies_type.rb +45 -0
  14. data/app/commands/decidim/assemblies/admin/import_assembly.rb +72 -0
  15. data/app/commands/decidim/assemblies/admin/notify_role_assigned_to_assembly.rb +22 -0
  16. data/app/commands/decidim/assemblies/admin/update_assemblies_setting.rb +46 -0
  17. data/app/commands/decidim/assemblies/admin/update_assemblies_type.rb +46 -0
  18. data/app/commands/decidim/assemblies/admin/update_assembly.rb +11 -7
  19. data/app/commands/decidim/assemblies/admin/update_assembly_admin.rb +2 -1
  20. data/app/controllers/concerns/decidim/assemblies/admin/filterable.rb +30 -0
  21. data/app/controllers/decidim/assemblies/admin/assemblies_controller.rb +8 -23
  22. data/app/controllers/decidim/assemblies/admin/assemblies_settings_controller.rb +49 -0
  23. data/app/controllers/decidim/assemblies/admin/assemblies_types_controller.rb +107 -0
  24. data/app/controllers/decidim/assemblies/admin/assembly_exports_controller.rb +24 -0
  25. data/app/controllers/decidim/assemblies/admin/assembly_imports_controller.rb +31 -0
  26. data/app/controllers/decidim/assemblies/admin/assembly_user_roles_controller.rb +2 -1
  27. data/app/controllers/decidim/assemblies/assemblies_controller.rb +6 -2
  28. data/app/controllers/decidim/assemblies/{assembly_widgets_controller.rb → widgets_controller.rb} +2 -2
  29. data/app/events/decidim/assemblies/create_assembly_member_event.rb +17 -0
  30. data/app/events/decidim/role_assigned_to_assembly_event.rb +28 -0
  31. data/app/forms/decidim/assemblies/admin/assemblies_setting_form.rb +14 -0
  32. data/app/forms/decidim/assemblies/admin/assemblies_type_form.rb +17 -0
  33. data/app/forms/decidim/assemblies/admin/assembly_form.rb +37 -19
  34. data/app/forms/decidim/assemblies/admin/assembly_import_form.rb +83 -0
  35. data/app/forms/decidim/assemblies/admin/assembly_user_role_form.rb +8 -2
  36. data/app/helpers/decidim/assemblies/admin/assemblies_helper.rb +6 -0
  37. data/app/helpers/decidim/assemblies/assemblies_helper.rb +6 -3
  38. data/app/helpers/decidim/assemblies/filter_assemblies_helper.rb +10 -7
  39. data/app/models/decidim/assemblies_setting.rb +17 -0
  40. data/app/models/decidim/assemblies_type.rb +27 -0
  41. data/app/models/decidim/assembly.rb +26 -3
  42. data/app/models/decidim/assembly_user_role.rb +1 -1
  43. data/app/permissions/decidim/assemblies/permissions.rb +60 -2
  44. data/app/presenters/decidim/assemblies/admin_log/assemblies_setting_presenter.rb +27 -0
  45. data/app/presenters/decidim/assemblies/admin_log/assemblies_type_presenter.rb +43 -0
  46. data/app/presenters/decidim/assemblies/admin_log/assembly_presenter.rb +1 -2
  47. data/app/presenters/decidim/assemblies/assembly_presenter.rb +29 -0
  48. data/app/presenters/decidim/assemblies/assembly_stats_presenter.rb +1 -1
  49. data/app/presenters/decidim/log/value_types/assembly_type_presenter.rb +29 -0
  50. data/app/queries/decidim/assemblies/filtered_assemblies.rb +2 -2
  51. data/app/queries/decidim/assemblies/parent_assemblies_for_select.rb +42 -0
  52. data/app/serializers/decidim/assemblies/assembly_importer.rb +172 -0
  53. data/app/serializers/decidim/assemblies/assembly_serializer.rb +147 -0
  54. data/app/services/decidim/assemblies/assembly_search.rb +3 -3
  55. data/app/types/decidim/assemblies/assemblies_type_type.rb +17 -0
  56. data/app/types/decidim/assemblies/assembly_member_type.rb +29 -0
  57. data/app/types/decidim/assemblies/assembly_type.rb +67 -0
  58. data/app/views/decidim/assemblies/_filter_by_type.html.erb +11 -7
  59. data/app/views/decidim/assemblies/admin/assemblies/_form.html.erb +2 -6
  60. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +18 -4
  61. data/app/views/decidim/assemblies/admin/assemblies_settings/_form.html.erb +10 -0
  62. data/app/views/decidim/assemblies/admin/assemblies_settings/edit.html.erb +6 -0
  63. data/app/views/decidim/assemblies/admin/assemblies_types/_form.html.erb +11 -0
  64. data/app/views/decidim/assemblies/admin/assemblies_types/edit.html.erb +6 -0
  65. data/app/views/decidim/assemblies/admin/assemblies_types/index.html.erb +43 -0
  66. data/app/views/decidim/assemblies/admin/assemblies_types/new.html.erb +7 -0
  67. data/app/views/decidim/assemblies/admin/assembly_imports/_form.html.erb +46 -0
  68. data/app/views/decidim/assemblies/admin/assembly_imports/new.html.erb +7 -0
  69. data/app/views/decidim/assemblies/admin/assembly_members/index.html.erb +30 -30
  70. data/app/views/decidim/assemblies/assemblies/_parent_assemblies.html.erb +2 -2
  71. data/app/views/decidim/assemblies/assemblies/_promoted_assembly.html.erb +3 -3
  72. data/app/views/decidim/assemblies/assemblies/index.html.erb +11 -8
  73. data/app/views/decidim/assemblies/assemblies/show.html.erb +11 -14
  74. data/app/views/decidim/assemblies/assembly_members/index.html.erb +1 -1
  75. data/app/views/layouts/decidim/admin/assemblies.html.erb +22 -0
  76. data/app/views/layouts/decidim/admin/assembly.html.erb +2 -2
  77. data/app/views/layouts/decidim/assembly.html.erb +1 -0
  78. data/config/locales/am-ET.yml +1 -0
  79. data/config/locales/ar.yml +24 -0
  80. data/config/locales/bg-BG.yml +7 -0
  81. data/config/locales/bg.yml +7 -0
  82. data/config/locales/ca.yml +100 -0
  83. data/config/locales/cs.yml +135 -35
  84. data/config/locales/da-DK.yml +1 -0
  85. data/config/locales/da.yml +1 -0
  86. data/config/locales/de.yml +99 -0
  87. data/config/locales/el.yml +475 -0
  88. data/config/locales/en.yml +100 -0
  89. data/config/locales/eo.yml +1 -0
  90. data/config/locales/es-MX.yml +100 -0
  91. data/config/locales/es-PY.yml +100 -0
  92. data/config/locales/es.yml +100 -0
  93. data/config/locales/et-EE.yml +1 -0
  94. data/config/locales/et.yml +1 -0
  95. data/config/locales/eu.yml +11 -0
  96. data/config/locales/fi-plain.yml +100 -0
  97. data/config/locales/fi.yml +104 -4
  98. data/config/locales/fr-CA.yml +478 -0
  99. data/config/locales/fr.yml +100 -0
  100. data/config/locales/ga-IE.yml +1 -0
  101. data/config/locales/gl.yml +11 -0
  102. data/config/locales/hr-HR.yml +1 -0
  103. data/config/locales/hr.yml +1 -0
  104. data/config/locales/hu.yml +85 -2
  105. data/config/locales/id-ID.yml +11 -0
  106. data/config/locales/is-IS.yml +11 -0
  107. data/config/locales/is.yml +263 -0
  108. data/config/locales/it.yml +171 -74
  109. data/config/locales/ja-JP.yml +471 -0
  110. data/config/locales/ja.yml +471 -0
  111. data/config/locales/ko-KR.yml +1 -0
  112. data/config/locales/ko.yml +1 -0
  113. data/config/locales/lt-LT.yml +1 -0
  114. data/config/locales/lt.yml +1 -0
  115. data/config/locales/lv.yml +454 -0
  116. data/config/locales/mt-MT.yml +1 -0
  117. data/config/locales/mt.yml +1 -0
  118. data/config/locales/nl.yml +97 -0
  119. data/config/locales/no.yml +107 -10
  120. data/config/locales/om-ET.yml +1 -0
  121. data/config/locales/pl.yml +250 -151
  122. data/config/locales/pt-BR.yml +12 -1
  123. data/config/locales/pt.yml +217 -118
  124. data/config/locales/ro-RO.yml +441 -0
  125. data/config/locales/ru.yml +11 -0
  126. data/config/locales/sk-SK.yml +168 -0
  127. data/config/locales/sk.yml +172 -0
  128. data/config/locales/sl.yml +283 -0
  129. data/config/locales/so-SO.yml +1 -0
  130. data/config/locales/sr-CS.yml +73 -0
  131. data/config/locales/sv.yml +188 -88
  132. data/config/locales/ti-ER.yml +1 -0
  133. data/config/locales/tr-TR.yml +30 -0
  134. data/config/locales/uk.yml +11 -0
  135. data/config/locales/vi-VN.yml +1 -0
  136. data/config/locales/vi.yml +1 -0
  137. data/config/locales/zh-CN.yml +471 -0
  138. data/config/locales/zh-TW.yml +1 -0
  139. data/db/migrate/20200108113855_create_decidim_assembly_types.rb +19 -0
  140. data/db/migrate/20200108123050_migrate_decidim_assembly_types.rb +83 -0
  141. data/db/migrate/20200320105906_index_foreign_keys_in_decidim_assemblies.rb +7 -0
  142. data/db/migrate/20200320105907_index_foreign_keys_in_decidim_assembly_user_roles.rb +7 -0
  143. data/db/migrate/20200416132109_remove_legacy_decidim_assembly_type.rb +8 -0
  144. data/db/migrate/20200430202456_create_decidim_assemblies_settings.rb +10 -0
  145. data/db/seeds/city.jpeg +0 -0
  146. data/db/seeds/city2.jpeg +0 -0
  147. data/lib/decidim/assemblies/admin_engine.rb +10 -0
  148. data/lib/decidim/assemblies/engine.rb +8 -1
  149. data/lib/decidim/assemblies/participatory_space.rb +29 -15
  150. data/lib/decidim/assemblies/query_extensions.rb +40 -0
  151. data/lib/decidim/assemblies/test/factories.rb +32 -5
  152. data/lib/decidim/assemblies/version.rb +1 -1
  153. metadata +90 -12
  154. data/app/views/decidim/assemblies/assembly_widgets/show.html.erb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bce95b5a50ca5a51acc8ec5006c31433e1221e6c4261797ceeb55a4440df460
4
- data.tar.gz: 33e3a8271d02f6f50816d6a554212877d50ecccd96835e291b9e24675011d1ad
3
+ metadata.gz: 14a57e28c47e324ad6eccfae0739b11f694a592524897b3050b9c2298be03d9b
4
+ data.tar.gz: ef3a2ac737a5a09fda27ce64745897adc0e72ad6fffbd5d1d9532c178308aaf3
5
5
  SHA512:
6
- metadata.gz: fe93f7eab23f14845eb11443873be2dff139b860c021284197a50b1bbc0dd4e49715cd0a357cd903c0a5123221c74db4c2e832b8993878fba4b611613eaed698
7
- data.tar.gz: f2aa517dd035ff6c746e545f15f54e7c93ddc3778ae90c63f29444554ee1fc8c9d8d60679f69a2864f629ecb7e60721d36e7b7185ce0b9f305fbe1e72412f6d3
6
+ metadata.gz: c35cf008d9f11ed052bf64d1a5cd271893c8a75355ea721b716940055fe3e4872cb72f27918073fd6f0ae29ee5624e1e4ef14d634579f77ebd01e443a63588a7
7
+ data.tar.gz: '002951e359e52ced0da4355357b0418bd9a3e8b79ac65499a1dd6cd68a6de70a316e17b9135ce54f07b72866a00fb7666d83070cdc039ce6afa4a7bdf819f463'
@@ -1,3 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 34">
2
- <path d="M31.593 20.937c.338-1.255.518-2.575.518-3.937 0-8.346-6.765-15.111-15.111-15.111S1.889 8.654 1.889 17c0 1.362.18 2.682.518 3.937a10.378 10.378 0 0 1 2.712-.36c2.422 0 4.717.835 6.55 2.328a10.348 10.348 0 0 1 5.177-1.383c1.95 0 3.786.542 5.357 1.49a10.338 10.338 0 0 1 6.678-2.434c.925 0 1.836.121 2.712.359zm-.606 1.793a8.487 8.487 0 0 0-2.106-.263 8.448 8.448 0 0 0-5.107 1.707 10.355 10.355 0 0 1 2.913 4.425 15.151 15.151 0 0 0 4.3-5.869zm-27.974 0c.9 2.195 2.302 4.13 4.062 5.665a10.357 10.357 0 0 1 2.982-4.345 8.45 8.45 0 0 0-4.938-1.583c-.718 0-1.424.089-2.106.263zm23.805 8.15a.94.94 0 0 1-.324.224A16.921 16.921 0 0 1 17 34C7.611 34 0 26.389 0 17S7.611 0 17 0s17 7.611 17 17c0 5.732-2.836 10.8-7.182 13.88zm-1.75-1.1a8.492 8.492 0 0 0-16.398-.17A15.04 15.04 0 0 0 17 32.11a15.04 15.04 0 0 0 8.067-2.331zm-3.042-14.33a5.18 5.18 0 1 1-10.36 0 5.18 5.18 0 0 1 10.36 0zm-1.89 0a3.291 3.291 0 1 0-6.582 0 3.291 3.291 0 0 0 6.583 0zm-11.333.944a3.062 3.062 0 1 1-6.124.001 3.062 3.062 0 0 1 6.124-.001zm-1.888 0a1.173 1.173 0 1 0-2.347 0 1.173 1.173 0 0 0 2.347 0zm18.28 0a3.063 3.063 0 1 1 6.127.002 3.063 3.063 0 0 1-6.126-.002zm1.89 0a1.174 1.174 0 1 0 2.348 0 1.174 1.174 0 0 0-2.348 0z"/>
3
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34 34"><path d="M31.593 20.937c.338-1.255.518-2.575.518-3.937 0-8.346-6.765-15.111-15.111-15.111S1.889 8.654 1.889 17c0 1.362.18 2.682.518 3.937a10.378 10.378 0 0 1 2.712-.36c2.422 0 4.717.835 6.55 2.328a10.348 10.348 0 0 1 5.177-1.383c1.95 0 3.786.542 5.357 1.49a10.338 10.338 0 0 1 6.678-2.434c.925 0 1.836.121 2.712.359zm-.606 1.793a8.487 8.487 0 0 0-2.106-.263 8.448 8.448 0 0 0-5.107 1.707 10.355 10.355 0 0 1 2.913 4.425 15.151 15.151 0 0 0 4.3-5.869zm-27.974 0c.9 2.195 2.302 4.13 4.062 5.665a10.357 10.357 0 0 1 2.982-4.345 8.45 8.45 0 0 0-4.938-1.583c-.718 0-1.424.089-2.106.263zm23.805 8.15a.94.94 0 0 1-.324.224A16.921 16.921 0 0 1 17 34C7.611 34 0 26.389 0 17S7.611 0 17 0s17 7.611 17 17c0 5.732-2.836 10.8-7.182 13.88zm-1.75-1.1a8.492 8.492 0 0 0-16.398-.17A15.04 15.04 0 0 0 17 32.11a15.04 15.04 0 0 0 8.067-2.331zm-3.042-14.33a5.18 5.18 0 1 1-10.36 0 5.18 5.18 0 0 1 10.36 0zm-1.89 0a3.291 3.291 0 1 0-6.582 0 3.291 3.291 0 0 0 6.583 0zm-11.333.944a3.062 3.062 0 1 1-6.124.001 3.062 3.062 0 0 1 6.124-.001zm-1.888 0a1.173 1.173 0 1 0-2.347 0 1.173 1.173 0 0 0 2.347 0zm18.28 0a3.063 3.063 0 1 1 6.127.002 3.063 3.063 0 0 1-6.126-.002zm1.89 0a1.174 1.174 0 1 0 2.348 0 1.174 1.174 0 0 0-2.348 0z"/></svg>
@@ -0,0 +1,698 @@
1
+ /* eslint-disable require-jsdoc, max-lines, no-return-assign, func-style, id-length, no-plusplus, no-use-before-define, no-negated-condition, init-declarations, no-invalid-this, no-param-reassign, no-ternary, multiline-ternary, no-nested-ternary, no-eval, no-extend-native, prefer-reflect */
2
+ /* eslint dot-location: ["error", "property"], no-negated-condition: "error" */
3
+ /* eslint no-unused-expressions: ["error", { "allowTernary": true }] */
4
+ /* eslint no-unused-vars: ["error", { "args": "none" }] */
5
+ /* global d3 */
6
+
7
+ // = require_self
8
+ // = require d3
9
+ ((exports) => {
10
+ const { Decidim: { Visualizations: render } } = exports;
11
+
12
+ // lib
13
+ const renderOrgCharts = () => {
14
+ const $orgChartContainer = $(".js-orgchart")
15
+ const $btnReset = $(".js-reset-orgchart")
16
+
17
+ let dataDepicted = null
18
+ let fake = false
19
+ let orgchart = {}
20
+
21
+ // lib - https://bl.ocks.org/bumbeishvili/b96ba47ea21d14dfce6ebb859b002d3a
22
+ const renderChartCollapsibleNetwork = (params) => {
23
+
24
+ // exposed variables
25
+ let attrs = {
26
+ id: `id${Math.floor(Math.random() * 1000000)}`,
27
+ svgWidth: 960,
28
+ svgHeight: 600,
29
+ marginTop: 0,
30
+ marginBottom: 5,
31
+ marginRight: 0,
32
+ marginLeft: 30,
33
+ container: "body",
34
+ distance: 150,
35
+ hiddenChildLevel: 1,
36
+ hoverOpacity: 0.2,
37
+ maxTextDisplayZoomLevel: 1,
38
+ lineStrokeWidth: 1.5,
39
+ fakeRoot: false,
40
+ nodeGutter: { x: 16, y: 8 },
41
+ childrenIndicatorRadius: 15,
42
+ fakeBorderWidth: 32,
43
+ data: null
44
+ }
45
+
46
+ /* ############### IF EXISTS OVERWRITE ATTRIBUTES FROM PASSED PARAM ####### */
47
+
48
+ let attrKeys = Object.keys(attrs)
49
+ attrKeys.forEach(function (key) {
50
+ if (params && params[key]) {
51
+ attrs[key] = params[key]
52
+ }
53
+ })
54
+
55
+ // innerFunctions which will update visuals
56
+ let updateData
57
+ let collapse, expand
58
+ let filter
59
+ let hierarchy = {}
60
+
61
+ // main chart object
62
+ let main = function (selection) {
63
+ selection.each(function scope() {
64
+
65
+ // calculated properties
66
+ let calc = {}
67
+ calc.chartLeftMargin = attrs.marginLeft
68
+ calc.chartTopMargin = attrs.marginTop
69
+ calc.chartWidth = attrs.svgWidth - attrs.marginRight - calc.chartLeftMargin
70
+ calc.chartHeight = attrs.svgHeight - attrs.marginBottom - calc.chartTopMargin
71
+
72
+ // ########################## HIERARCHY STUFF #########################
73
+ hierarchy.root = d3.hierarchy(attrs.data.root)
74
+
75
+ // ########################### BEHAVIORS #########################
76
+ let behaviors = {}
77
+ // behaviors.zoom = d3.zoom().scaleExtent([0.75, 100, 8]).on("zoom", zoomed)
78
+ behaviors.drag = d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended)
79
+
80
+ // ########################### LAYOUTS #########################
81
+ let layouts = {}
82
+
83
+ // custom radial layout
84
+ layouts.radial = d3.radial()
85
+
86
+ // ########################### FORCE STUFF #########################
87
+ let force = {}
88
+ force.link = d3.forceLink().id((d) => d.id)
89
+ force.charge = d3.forceManyBody().strength(-240)
90
+ force.center = d3.forceCenter(calc.chartWidth / 2, calc.chartHeight / 2)
91
+
92
+ // prevent collide
93
+ force.collide = d3.forceCollide().radius((d) => {
94
+ // Creates an invented radius based on element measures: diagonal = 2 * radius = sqrt(width^2, height^2)
95
+ let base = (d.bbox || {}).width + (attrs.nodeGutter.x * 2)
96
+ let height = (d.bbox || {}).height + (attrs.nodeGutter.y * 2)
97
+ let diagonal = Math.sqrt(Math.pow(base, 2) + Math.pow(height, 2))
98
+ let fakeRadius = (diagonal / 2)
99
+
100
+ // return d3.max([attrs.nodeDistance * 3, fakeRadius])
101
+ return fakeRadius * 1.5
102
+ })
103
+
104
+ // manually set x positions (which is calculated using custom radial layout)
105
+ force.x = d3.forceX()
106
+ .strength(0.5)
107
+ .x(function (d) {
108
+
109
+ // if node does not have children and is channel (depth=2) , then position it on parent's coordinate
110
+ if (!d.children && d.depth > 2) {
111
+ if (d.parent) {
112
+ d = d.parent
113
+ }
114
+ }
115
+
116
+ // custom circle projection - radius will be - (d.depth - 1) * 150
117
+ return projectCircle(d.proportion, (d.depth - 1) * attrs.distance)[0]
118
+ })
119
+
120
+ // manually set y positions (which is calculated using d3.cluster)
121
+ force.y = d3.forceY()
122
+ .strength(0.5)
123
+ .y(function (d) {
124
+
125
+ // if node does not have children and is channel (depth=2) , then position it on parent's coordinate
126
+ if (!d.children && d.depth > 2) {
127
+ if (d.parent) {
128
+ d = d.parent
129
+ }
130
+ }
131
+
132
+ // custom circle projection - radius will be - (d.depth - 1) * 150
133
+ return projectCircle(d.proportion, (d.depth - 1) * attrs.distance)[1]
134
+ })
135
+
136
+ // --------------------------------- INITIALISE FORCE SIMULATION ----------------------------
137
+
138
+ // get based on top parameter simulation
139
+ force.simulation = d3.forceSimulation()
140
+ .force("link", force.link)
141
+ .force("charge", force.charge)
142
+ .force("center", force.center)
143
+ .force("collide", force.collide)
144
+ .force("x", force.x)
145
+ .force("y", force.y)
146
+
147
+ // ########################### HIERARCHY STUFF #########################
148
+
149
+ // flatten root
150
+ let arr = flatten(hierarchy.root)
151
+
152
+ // hide members based on their depth
153
+ arr.forEach((d) => {
154
+ // Hide fake root node
155
+ if ((attrs.fakeRoot) && (d.depth === 1)) {
156
+ d.hidden = true
157
+ }
158
+
159
+ if (d.depth > attrs.hiddenChildLevel) {
160
+ d._children = d.children
161
+ d.children = null
162
+ }
163
+ })
164
+
165
+ // #################################### DRAWINGS #######################
166
+
167
+ // drawing containers
168
+ let container = d3.select(this)
169
+
170
+ // add svg
171
+ let svg = container.patternify({ tag: "svg", selector: "svg-chart-container" })
172
+ .attr("width", attrs.svgWidth)
173
+ .attr("height", attrs.svgHeight)
174
+ // .call(behaviors.zoom)
175
+
176
+ // add container g element
177
+ let chart = svg.patternify({ tag: "g", selector: "chart" })
178
+ .attr("transform", `translate(${calc.chartLeftMargin},${calc.chartTopMargin})`)
179
+
180
+ // ################################ Chart Content Drawing ##################################
181
+
182
+ // link wrapper
183
+ let linksWrapper = chart.patternify({ tag: "g", selector: "links-wrapper" })
184
+
185
+ // node wrapper
186
+ let nodesWrapper = chart.patternify({ tag: "g", selector: "nodes-wrapper" })
187
+ let links, nodes
188
+
189
+ // reusable function which updates visual based on data change
190
+ update()
191
+
192
+ // update visual based on data change
193
+ function update(clickedNode) {
194
+
195
+ // Show/hide reset button
196
+ (clickedNode) ? $btnReset.removeClass("invisible") : $btnReset.addClass("invisible")
197
+
198
+ // set xy and proportion properties with custom radial layout
199
+ layouts.radial(hierarchy.root)
200
+
201
+ // nodes and links array
202
+ let nodesArr = flatten(hierarchy.root, true)
203
+ .orderBy((d) => d.depth)
204
+ .filter((d) => !d.hidden)
205
+
206
+ let linksArr = hierarchy.root.links()
207
+ .filter((d) => !d.source.hidden)
208
+ .filter((d) => !d.target.hidden)
209
+
210
+ // make new nodes to appear near the parents
211
+ nodesArr.forEach(function (d) {
212
+ if (clickedNode && clickedNode.id === (d.parent && d.parent.id)) {
213
+ d.x = d.parent.x
214
+ d.y = d.parent.y
215
+ }
216
+ })
217
+
218
+ // links
219
+ links = linksWrapper.selectAll(".link")
220
+ .data(linksArr, (d) => d.target.id)
221
+ links.exit().remove()
222
+
223
+ links = links.enter()
224
+ .append("line")
225
+ .attr("class", "link")
226
+ .merge(links)
227
+
228
+ // node groups
229
+ nodes = nodesWrapper.selectAll(".node")
230
+ .data(nodesArr, (d) => d.id)
231
+ nodes.exit().remove()
232
+
233
+ let enteredNodes = nodes.enter()
234
+ .append("g")
235
+ .attr("class", "node")
236
+
237
+ // bind event handlers
238
+ enteredNodes
239
+ .on("click", nodeClick)
240
+ .on("mouseenter", nodeMouseEnter)
241
+ .on("mouseleave", nodeMouseLeave)
242
+ .call(behaviors.drag)
243
+
244
+ // channels grandchildren
245
+ enteredNodes.append("rect")
246
+ .attr("class", "as-card")
247
+ .attr("rx", 4)
248
+ .attr("ry", 4)
249
+
250
+ enteredNodes.append("text")
251
+ .attr("class", "as-text")
252
+ .text((d) => d.data.name)
253
+
254
+ enteredNodes.selectAll("text").each(function(d) {
255
+ d.bbox = this.getBBox()
256
+ })
257
+
258
+ enteredNodes.selectAll("rect")
259
+ .attr("x", (d) => d.bbox.x - attrs.nodeGutter.x)
260
+ .attr("y", (d) => d.bbox.y - attrs.nodeGutter.y)
261
+ .attr("width", (d) => d.bbox.width + (2 * attrs.nodeGutter.x))
262
+ .attr("height", (d) => d.bbox.height + (2 * attrs.nodeGutter.y))
263
+
264
+ // append circle & text only when there are children
265
+ enteredNodes
266
+ .append("circle")
267
+ .filter((d) => Boolean(d.children) || Boolean(d._children))
268
+ .attr("class", "as-circle")
269
+ .attr("r", attrs.childrenIndicatorRadius)
270
+ .attr("cx", (d) => d.bbox.x + d.bbox.width + attrs.nodeGutter.x)
271
+ .attr("cy", (d) => d.bbox.y + d.bbox.height + attrs.nodeGutter.y)
272
+
273
+ enteredNodes
274
+ .append("text")
275
+ .filter((d) => Boolean(d.children) || Boolean(d._children))
276
+ .attr("class", "as-text")
277
+ .attr("dx", (d) => d.bbox.x + d.bbox.width + attrs.nodeGutter.x)
278
+ .attr("dy", attrs.childrenIndicatorRadius + 3)
279
+ .text((d) => d3.max([(d.children || {}).length, (d._children || {}).length]))
280
+
281
+ // merge node groups and style it
282
+ nodes = enteredNodes.merge(nodes)
283
+
284
+ // force simulation
285
+ force.simulation.nodes(nodesArr).on("tick", ticked)
286
+
287
+ // links simulation
288
+ force.simulation.force("link").links(links).id((d) => d.id).distance(attrs.distance * 2).strength(2)
289
+ }
290
+
291
+ // ####################################### EVENT HANDLERS ########################
292
+
293
+ // zoom handler
294
+ // function zoomed() {
295
+ // // get transform event
296
+ // let transform = d3.event.transform
297
+ // attrs.lastTransform = transform
298
+ //
299
+ // // apply transform event props to the wrapper
300
+ // chart.attr("transform", transform)
301
+ //
302
+ // svg.selectAll(".node").attr("transform", (d) => `translate(${d.x},${d.y}) scale(${1 / (attrs.lastTransform ? attrs.lastTransform.k : 1)})`)
303
+ // svg.selectAll(".link").attr("stroke-width", attrs.lineStrokeWidth / (attrs.lastTransform ? attrs.lastTransform.k : 1))
304
+ // }
305
+
306
+ // tick handler
307
+ function ticked() {
308
+ const fakeBorderWidth = attrs.fakeBorderWidth
309
+ const maxXValueAvailable = (value) => Math.max(Math.min(calc.chartWidth - fakeBorderWidth, value), fakeBorderWidth)
310
+ const maxYValueAvailable = (value) => Math.max(Math.min(calc.chartHeight - fakeBorderWidth, value), fakeBorderWidth)
311
+ // set links position
312
+ links
313
+ .attr("x1", (d) => maxXValueAvailable(d.source.x))
314
+ .attr("y1", (d) => maxYValueAvailable(d.source.y))
315
+ .attr("x2", (d) => maxXValueAvailable(d.target.x))
316
+ .attr("y2", (d) => maxYValueAvailable(d.target.y))
317
+
318
+ // set nodes position
319
+ svg.selectAll(".node")
320
+ .attr("transform", (d) => `translate(${maxXValueAvailable(d.x)},${maxYValueAvailable(d.y)})`)
321
+ }
322
+
323
+ // handler drag start event
324
+ function dragstarted() {
325
+ // disable node fixing
326
+ nodes.each((d) => {
327
+ d.fx = null
328
+ d.fy = null
329
+ })
330
+ }
331
+
332
+ // handle dragging event
333
+ function dragged(d) {
334
+ // make dragged node fixed
335
+ d.fx = d3.event.x
336
+ d.fy = d3.event.y
337
+ }
338
+
339
+ // -------------------- handle drag end event ---------------
340
+ function dragended() {
341
+ // we are doing nothing, here , aren't we?
342
+ }
343
+
344
+ // -------------------------- node mouse hover handler ---------------
345
+ function nodeMouseEnter(d) {
346
+ // get links
347
+ let _links = hierarchy.root.links()
348
+
349
+ // get hovered node connected links
350
+ let connectedLinks = _links.filter((l) => l.source.id === d.id || l.target.id === d.id)
351
+
352
+ // get hovered node linked nodes
353
+ let linkedNodes = connectedLinks.map((s) => s.source.id).concat(connectedLinks.map((c) => c.target.id))
354
+
355
+ // reduce all other nodes opacity
356
+ nodesWrapper.selectAll(".node")
357
+ .filter((n) => linkedNodes.indexOf(n.id) === -1)
358
+ .attr("opacity", attrs.hoverOpacity)
359
+
360
+ // reduce all other links opacity
361
+ linksWrapper.selectAll(".link")
362
+ .attr("opacity", attrs.hoverOpacity)
363
+
364
+ // highlight hovered nodes connections
365
+ linksWrapper.selectAll(".link")
366
+ .filter((l) => l.source.id === d.id || l.target.id === d.id)
367
+ .attr("opacity", 1)
368
+ }
369
+
370
+ // --------------- handle mouseleave event ---------------
371
+ function nodeMouseLeave() {
372
+ // return things back to normal
373
+ nodesWrapper.selectAll(".node")
374
+ .attr("opacity", 1)
375
+ linksWrapper.selectAll(".link")
376
+ .attr("opacity", 1)
377
+ }
378
+
379
+ // --------------- handle node click event ---------------
380
+ function nodeClick(d) {
381
+ // free fixed nodes
382
+ nodes.each((di) => {
383
+ di.fx = null
384
+ di.fy = null
385
+ })
386
+
387
+ // collapse or expand node
388
+ if (d.children) {
389
+ collapse(d)
390
+ } else if (d._children) {
391
+ expand(d)
392
+ } else {
393
+ // nothing is to collapse or expand
394
+ }
395
+
396
+ freeNodes()
397
+ }
398
+
399
+ // ######################################### UTIL FUNCS ##################################
400
+ updateData = function () {
401
+ main.run()
402
+ }
403
+
404
+ collapse = function (d, deep = false) {
405
+ if (d.children) {
406
+ if (deep) {
407
+ d.children.forEach((e) => collapse(e, true))
408
+ }
409
+
410
+ d._children = d.children
411
+ d.children = null
412
+ }
413
+
414
+ update(d)
415
+ force.simulation.restart()
416
+ force.simulation.alphaTarget(0.15)
417
+ }
418
+
419
+ expand = function (d, deep = false) {
420
+ if (d._children) {
421
+ if (deep) {
422
+ d._children.forEach((e) => expand(e, true))
423
+ }
424
+
425
+ d.children = d._children
426
+ d._children = null
427
+ }
428
+
429
+ update(d)
430
+ force.simulation.restart()
431
+ force.simulation.alphaTarget(0.15)
432
+ }
433
+
434
+ // function slowDownNodes() {
435
+ // force.simulation.alphaTarget(0.05)
436
+ // }
437
+
438
+ // function speedUpNodes() {
439
+ // force.simulation.alphaTarget(0.45)
440
+ // }
441
+
442
+ function freeNodes() {
443
+ d3.selectAll(".node").each((n) => {
444
+ n.fx = null
445
+ n.fy = null
446
+ })
447
+ }
448
+
449
+ function projectCircle(value, radius) {
450
+ let r = radius || 0
451
+ let corner = value * 2 * Math.PI
452
+ return [Math.sin(corner) * r, -Math.cos(corner) * r]
453
+ }
454
+
455
+ // recursively loop on children and extract nodes as an array
456
+ function flatten(root, clustered) {
457
+ let nodesArray = []
458
+ let i = 0
459
+ function recurse(node, depth) {
460
+ if (node.children) {
461
+ node.children.forEach(function (child) {
462
+ recurse(child, depth + 1)
463
+ })
464
+ }
465
+
466
+ if (!node.id) {
467
+ node.id = ++i
468
+ } else {
469
+ ++i
470
+ }
471
+
472
+ node.depth = depth
473
+ if (clustered) {
474
+ if (!node.cluster) {
475
+ // if cluster coordinates are not set, set it
476
+ node.cluster = { x: node.x, y: node.y }
477
+ }
478
+ }
479
+ nodesArray.push(node)
480
+ }
481
+ recurse(root, 1)
482
+ return nodesArray
483
+ }
484
+
485
+ function debug() {
486
+ if (attrs.isDebug) {
487
+ // stringify func
488
+ let stringified = String(scope)
489
+
490
+ // parse variable names
491
+ let groupVariables = stringified
492
+ // match var x-xx= {}
493
+ .match(/var\s+([\w])+\s*=\s*{\s*}/gi)
494
+ // match xxx
495
+ .map((d) => d.match(/\s+\w*/gi).filter((s) => s.trim()))
496
+ // get xxx
497
+ .map((v) => v[0].trim())
498
+
499
+ // assign local variables to the scope
500
+ groupVariables.forEach((v) => {
501
+ main[`P_${v}`] = eval(v)
502
+ })
503
+ }
504
+ }
505
+
506
+ debug()
507
+
508
+ })
509
+ }
510
+
511
+ // ----------- PROTOTYEPE FUNCTIONS ----------------------
512
+ d3.selection.prototype.patternify = function (_params) {
513
+ let selector = _params.selector
514
+ let elementTag = _params.tag
515
+ let _data = _params.data || [selector]
516
+
517
+ // pattern in action
518
+ let selection = this.selectAll(`.${selector}`).data(_data)
519
+ selection.exit().remove()
520
+ selection = selection.enter().append(elementTag).merge(selection)
521
+ selection.attr("class", selector)
522
+
523
+ return selection
524
+ }
525
+
526
+ // custom radial layout
527
+ d3.radial = function () {
528
+ return function (root) {
529
+
530
+ recurse(root, 0, 1)
531
+
532
+ function recurse(node, min, max) {
533
+ node.proportion = (max + min) / 2
534
+ if (!node.x) {
535
+
536
+ // if node has parent, match entered node positions to it's parent
537
+ if (node.parent) {
538
+ node.x = node.parent.x
539
+ } else {
540
+ node.x = 0
541
+ }
542
+ }
543
+
544
+ // if node had parent, match entered node positions to it's parent
545
+ if (!node.y) {
546
+ if (node.parent) {
547
+ node.y = node.parent.y
548
+ } else {
549
+ node.y = 0
550
+ }
551
+ }
552
+
553
+ // recursively do the same for children
554
+ if (node.children) {
555
+ let offset = (max - min) / node.children.length
556
+ node.children.forEach(function (child, i) {
557
+ let newMin = min + (offset * i)
558
+ let newMax = newMin + offset
559
+
560
+ recurse(child, newMin, newMax)
561
+ })
562
+ }
563
+ }
564
+ }
565
+ }
566
+
567
+ // https://github.com/bumbeishvili/d3js-boilerplates#orderby
568
+ Array.prototype.orderBy = function (func) {
569
+ this.sort((_a, _b) => {
570
+ let a = func(_a)
571
+ let b = func(_b)
572
+ if (typeof a === "string" || a instanceof String) {
573
+ return a.localeCompare(b)
574
+ }
575
+ return a - b
576
+ })
577
+
578
+ return this
579
+ }
580
+
581
+ // ########################## BOILEPLATE STUFF ################
582
+
583
+ // dinamic keys functions
584
+ Object.keys(attrs).forEach((key) => {
585
+ // Attach variables to main function
586
+ return main[key] = function (_) {
587
+ let string = `attrs['${key}'] = _`
588
+
589
+ if (!arguments.length) {
590
+ return eval(` attrs['${key}'];`)
591
+ }
592
+
593
+ eval(string)
594
+
595
+ return main
596
+ }
597
+ })
598
+
599
+ // set attrs as property
600
+ main.attrs = attrs
601
+
602
+ // debugging visuals
603
+ main.debug = function (isDebug) {
604
+ attrs.isDebug = isDebug
605
+ if (isDebug) {
606
+ if (!window.charts) {
607
+ window.charts = []
608
+ }
609
+ window.charts.push(main)
610
+ }
611
+ return main
612
+ }
613
+
614
+ // exposed update functions
615
+ main.data = function (value) {
616
+ if (!arguments.length) {
617
+ return attrs.data
618
+ }
619
+
620
+ attrs.data = value
621
+ if (typeof updateData === "function") {
622
+ updateData()
623
+ }
624
+ return main
625
+ }
626
+
627
+ // run visual
628
+ main.run = function () {
629
+ d3.selectAll(attrs.container)
630
+ .call(main)
631
+ return main
632
+ }
633
+
634
+ main.filter = function (filterParams) {
635
+ if (!arguments.length) {
636
+ return attrs.filterParams
637
+ }
638
+
639
+ attrs.filterParams = filterParams
640
+ if (typeof filter === "function") {
641
+ filter()
642
+ }
643
+ return main
644
+ }
645
+
646
+ main.reset = function () {
647
+
648
+ hierarchy.root.children.forEach((e) => collapse(e, true))
649
+ main.run()
650
+
651
+ return main
652
+ }
653
+
654
+ return main
655
+ }
656
+
657
+ // initialization
658
+ $orgChartContainer.each((i, container) => {
659
+
660
+ let $container = $(container)
661
+ let width = $container.width()
662
+ let height = width / (16 / 9)
663
+
664
+ d3.json($container.data("url")).then((data) => {
665
+ // Make a fake previous node if the data entry is not hierarchical
666
+ if (data instanceof Array) {
667
+ fake = true
668
+ dataDepicted = {
669
+ name: null,
670
+ children: data
671
+ }
672
+ } else {
673
+ dataDepicted = data
674
+ }
675
+
676
+ orgchart = renderChartCollapsibleNetwork()
677
+ .svgHeight(height)
678
+ .svgWidth(width)
679
+ .fakeRoot(fake)
680
+ .container(`#${container.id}`)
681
+ .data({
682
+ root: dataDepicted
683
+ })
684
+ .debug(true)
685
+ .run()
686
+ })
687
+ })
688
+
689
+ // reset
690
+ $btnReset.click(function() {
691
+ orgchart.reset()
692
+ })
693
+ }
694
+
695
+ $(() => {
696
+ render(renderOrgCharts);
697
+ })
698
+ })(window);