decidim-assemblies 0.21.0 → 0.22.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 (81) 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 +4 -0
  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_assembly_admin.rb +2 -1
  10. data/app/commands/decidim/assemblies/admin/create_assembly_member.rb +12 -3
  11. data/app/commands/decidim/assemblies/admin/notify_role_assigned_to_assembly.rb +22 -0
  12. data/app/commands/decidim/assemblies/admin/update_assemblies_setting.rb +46 -0
  13. data/app/commands/decidim/assemblies/admin/update_assembly_admin.rb +2 -1
  14. data/app/controllers/decidim/assemblies/admin/assemblies_settings_controller.rb +49 -0
  15. data/app/controllers/decidim/assemblies/assemblies_controller.rb +5 -1
  16. data/app/events/decidim/assemblies/create_assembly_member_event.rb +17 -0
  17. data/app/events/decidim/role_assigned_to_assembly_event.rb +28 -0
  18. data/app/forms/decidim/assemblies/admin/assemblies_setting_form.rb +14 -0
  19. data/app/forms/decidim/assemblies/admin/assembly_form.rb +14 -4
  20. data/app/helpers/decidim/assemblies/assemblies_helper.rb +6 -3
  21. data/app/models/decidim/assemblies_setting.rb +17 -0
  22. data/app/permissions/decidim/assemblies/permissions.rb +30 -1
  23. data/app/presenters/decidim/assemblies/admin_log/assemblies_setting_presenter.rb +27 -0
  24. data/app/presenters/decidim/assemblies/assembly_stats_presenter.rb +1 -1
  25. data/app/queries/decidim/assemblies/parent_assemblies_for_select.rb +20 -1
  26. data/app/views/decidim/assemblies/_filter_by_type.html.erb +11 -9
  27. data/app/views/decidim/assemblies/admin/assemblies/index.html.erb +3 -2
  28. data/app/views/decidim/assemblies/admin/assemblies_settings/_form.html.erb +10 -0
  29. data/app/views/decidim/assemblies/admin/assemblies_settings/edit.html.erb +6 -0
  30. data/app/views/decidim/assemblies/admin/assembly_members/index.html.erb +2 -2
  31. data/app/views/decidim/assemblies/assemblies/_parent_assemblies.html.erb +2 -2
  32. data/app/views/decidim/assemblies/assemblies/_promoted_assembly.html.erb +3 -3
  33. data/app/views/decidim/assemblies/assemblies/index.html.erb +11 -8
  34. data/app/views/decidim/assemblies/assemblies/show.html.erb +9 -8
  35. data/app/views/decidim/assemblies/assembly_members/index.html.erb +1 -1
  36. data/app/views/layouts/decidim/admin/assemblies.html.erb +5 -0
  37. data/config/locales/bg-BG.yml +7 -0
  38. data/config/locales/ca.yml +46 -0
  39. data/config/locales/cs.yml +76 -30
  40. data/config/locales/da-DK.yml +1 -0
  41. data/config/locales/de.yml +69 -0
  42. data/config/locales/el.yml +455 -0
  43. data/config/locales/en.yml +46 -0
  44. data/config/locales/es-MX.yml +46 -0
  45. data/config/locales/es-PY.yml +46 -0
  46. data/config/locales/es.yml +46 -0
  47. data/config/locales/et-EE.yml +1 -0
  48. data/config/locales/fi-plain.yml +46 -0
  49. data/config/locales/fi.yml +47 -1
  50. data/config/locales/fr-CA.yml +456 -0
  51. data/config/locales/fr.yml +67 -0
  52. data/config/locales/ga-IE.yml +1 -0
  53. data/config/locales/hr-HR.yml +1 -0
  54. data/config/locales/hu.yml +35 -2
  55. data/config/locales/it.yml +134 -76
  56. data/config/locales/ja-JP.yml +452 -0
  57. data/config/locales/lt-LT.yml +1 -0
  58. data/config/locales/lv-LV.yml +454 -0
  59. data/config/locales/mt-MT.yml +1 -0
  60. data/config/locales/nl.yml +58 -0
  61. data/config/locales/no.yml +57 -10
  62. data/config/locales/pl.yml +212 -143
  63. data/config/locales/pt-BR.yml +1 -1
  64. data/config/locales/pt.yml +188 -119
  65. data/config/locales/ro-RO.yml +422 -0
  66. data/config/locales/sk-SK.yml +168 -0
  67. data/config/locales/sk.yml +172 -0
  68. data/config/locales/sl.yml +132 -0
  69. data/config/locales/sr-CS.yml +73 -0
  70. data/config/locales/sv.yml +156 -89
  71. data/db/migrate/20200320105906_index_foreign_keys_in_decidim_assemblies.rb +7 -0
  72. data/db/migrate/20200320105907_index_foreign_keys_in_decidim_assembly_user_roles.rb +7 -0
  73. data/db/migrate/20200416132109_remove_legacy_decidim_assembly_type.rb +8 -0
  74. data/db/migrate/20200430202456_create_decidim_assemblies_settings.rb +10 -0
  75. data/db/seeds/city.jpeg +0 -0
  76. data/db/seeds/city2.jpeg +0 -0
  77. data/lib/decidim/assemblies/admin_engine.rb +1 -0
  78. data/lib/decidim/assemblies/participatory_space.rb +6 -1
  79. data/lib/decidim/assemblies/test/factories.rb +5 -0
  80. data/lib/decidim/assemblies/version.rb +1 -1
  81. metadata +38 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59b7c6152c1d1c2c96dc32b800435039413c4a9ad9e91150dd5101251af5fdba
4
- data.tar.gz: 9d4370974c2dacb07d232fee8dcde3aa92a3b74e86b7aaf836860c32a3120ec2
3
+ metadata.gz: 1242c1f63ead3579b71e5b61ac1ae5b9244fc49d76b3a8b6ff5089b9911f0906
4
+ data.tar.gz: cf5a33dbc2767c1b6588400c316d8bcdac283ac0b4cf374d5bfce24735bca348
5
5
  SHA512:
6
- metadata.gz: 84c422de10b4da4aac9d0e3e7ec291bd32664641d8a163ab68072615c5d23816a7c804939b1023b930c9fbfec93cccdcb5a990a1ed62e28ba2ec5ce4c3e283ea
7
- data.tar.gz: 918d1933d6bc85ff6d3044a4db73a7576b187b5731e00fc2c9789e671466bbe560590410c72a4178280c9c50336195650633a63bdfa28f29332aa382df8a0b33
6
+ metadata.gz: df5b655442c857a964563d201b8bed5c6769e0f1345e6eb8874c8dbd6b5bd2407448d90caebbebb232093fe6ac940a4d020d2028c78f7d917ba2ad8c7b4d520d
7
+ data.tar.gz: ea5c8b9d8547798488e9e07f6c8a3c9641878ceab634e24b94c42203bedc7f9e1800696c3441b6778d8d8e92fb7382db03e0689c4a4ba6dcb02e3289666f8459
@@ -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);