promethee 1.0.20 → 1.1.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/promethee-editor.sass +26 -57
  3. data/app/assets/stylesheets/promethee-editor/_move.sass +102 -0
  4. data/app/assets/stylesheets/promethee-editor/_preview.sass +2 -3
  5. data/app/assets/stylesheets/promethee-editor/_write.sass +58 -0
  6. data/app/views/promethee/_edit.html.erb +34 -99
  7. data/app/views/promethee/_localize.html.erb +1 -1
  8. data/app/views/promethee/_show.html.erb +1 -2
  9. data/app/views/promethee/edit/_move.html.erb +17 -38
  10. data/app/views/promethee/edit/_preview.html.erb +4 -36
  11. data/app/views/promethee/edit/_write.html.erb +9 -25
  12. data/app/views/promethee/edit/inspector/_inspector.html.erb +11 -0
  13. data/app/views/promethee/edit/inspector/component/_column.html.erb +2 -2
  14. data/app/views/promethee/edit/inspector/component/_cover.html.erb +1 -1
  15. data/app/views/promethee/edit/inspector/component/_details.html.erb +1 -1
  16. data/app/views/promethee/edit/inspector/component/_image.html.erb +3 -3
  17. data/app/views/promethee/edit/inspector/component/_text.html.erb +2 -11
  18. data/app/views/promethee/edit/inspector/component/_video.html.erb +1 -1
  19. data/app/views/promethee/edit/move/_component.html.erb +3 -1
  20. data/app/views/promethee/edit/move/_components.html.erb +10 -4
  21. data/app/views/promethee/edit/move/component/_column.html.erb +11 -9
  22. data/app/views/promethee/edit/move/component/_cover.html.erb +1 -1
  23. data/app/views/promethee/edit/move/component/_details.html.erb +1 -1
  24. data/app/views/promethee/edit/move/component/_row.html.erb +2 -2
  25. data/app/views/promethee/edit/shared/_data.html.erb +1 -0
  26. data/app/views/promethee/edit/shared/_navbar.html.erb +13 -19
  27. data/app/views/promethee/edit/write/_add_button.html.erb +23 -31
  28. data/app/views/promethee/edit/write/_toolbar.html.erb +1 -1
  29. data/app/views/promethee/edit/write/component/_column.html.erb +5 -37
  30. data/app/views/promethee/edit/write/component/_cover.html.erb +3 -22
  31. data/app/views/promethee/edit/write/component/_details.html.erb +3 -22
  32. data/app/views/promethee/edit/write/component/_image.html.erb +4 -23
  33. data/app/views/promethee/edit/write/component/_row.html.erb +4 -25
  34. data/app/views/promethee/edit/write/component/_slider.html.erb +3 -20
  35. data/app/views/promethee/edit/write/component/_text.html.erb +5 -24
  36. data/app/views/promethee/edit/write/component/_video.html.erb +7 -6
  37. data/lib/promethee/data.rb +56 -100
  38. data/lib/promethee/rails/version.rb +1 -1
  39. metadata +6 -4
  40. data/app/assets/stylesheets/promethee-editor/_component.sass +0 -27
  41. data/app/assets/stylesheets/promethee-editor/_mover.sass +0 -85
@@ -1,5 +1,5 @@
1
1
  <script type="text/ng-template" id="promethee/write/toolbar">
2
2
  <div class="pull-right">
3
- <span ng-click="remove(component, components)" class="promethee-editor__button"><%= fa_icon 'window-close' %></span>
3
+ <span ng-click="remove(component, components)" class="promethee-editor__button"><%= fa_icon :times %></span>
4
4
  </div>
5
5
  </script>
@@ -1,5 +1,6 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/column">
2
2
  <div
3
+ ng-click="inspect(component, $event)"
3
4
  class="
4
5
  col
5
6
  col-md-{{component.attributes.size}}
@@ -7,47 +8,14 @@
7
8
  promethee-editor__component
8
9
  promethee-editor__component--column
9
10
  "
10
- ng-controller="ColumnController"
11
- ng-click="edit($event);"
12
- >
13
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
11
+ >
12
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
14
13
  <div class="promethee-editor__toolbar">
15
14
  Column
16
15
  <ng-include src="'promethee/write/toolbar'"></ng-include>
17
16
  </div>
18
-
19
17
  <ng-include src="'promethee/write/components'"></ng-include>
20
-
21
- <span
22
- type="button"
23
- class="btn btn-default btn-block"
24
- ng-click="addComponentTo(component.children)"
25
- >
26
- Add component
27
- </span>
18
+ <ng-include src="'promethee/write/add_button'"></ng-include>
28
19
  </div>
29
20
  </div>
30
- </script>
31
-
32
- <script>
33
- promethee.controller('ColumnController', ['$scope', 'inspected', function($scope, inspected) {
34
- Object.defineProperty($scope, 'editing', {
35
- get: function() {
36
- return inspected.component == $scope.component;
37
- }
38
- });
39
-
40
- $scope.edit = function(event) {
41
- event.stopPropagation();
42
- inspected.component = $scope.component;
43
- };
44
-
45
- $scope.complete = function() {
46
- inspected.component = null;
47
- };
48
-
49
- $scope.toggleEdit = function() {
50
- $scope[$scope.editing ? 'complete' : 'edit']();
51
- };
52
- }]);
53
- </script>
21
+ </script>
@@ -1,10 +1,10 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/cover">
2
2
  <div
3
3
  ng-controller="CoverController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="promethee-editor__component promethee-editor__component--cover"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Cover
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
@@ -42,26 +42,7 @@
42
42
  }
43
43
  });
44
44
 
45
- promethee.controller('CoverController', ['$scope', 'inspected', function($scopen, inspected) {
46
- Object.defineProperty($scope, 'editing', {
47
- get: function() {
48
- return inspected.component == $scope.component;
49
- }
50
- });
51
-
52
- $scope.edit = function(event) {
53
- event.stopPropagation();
54
- inspected.component = $scope.component;
55
- };
56
-
57
- $scope.complete = function() {
58
- inspected.component = null;
59
- };
60
-
61
- $scope.toggleEdit = function() {
62
- $scope[$scope.editing ? 'complete' : 'edit']();
63
- };
64
-
45
+ promethee.controller('CoverController', ['$scope', function($scopen) {
65
46
  $scope.options = {
66
47
  toolbar: [
67
48
  ['headline', ['style']],
@@ -1,10 +1,10 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/details">
2
2
  <div
3
3
  ng-controller="DetailsController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="promethee-editor__component promethee-editor__component--details"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Details
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
@@ -39,26 +39,7 @@
39
39
  }
40
40
  });
41
41
 
42
- promethee.controller('DetailsController', ['$scope', 'inspected', function($scope, inspected) {
43
- Object.defineProperty($scope, 'editing', {
44
- get: function() {
45
- return inspected.component == $scope.component;
46
- }
47
- });
48
-
49
- $scope.edit = function(event) {
50
- event.stopPropagation();
51
- inspected.component = $scope.component;
52
- };
53
-
54
- $scope.complete = function() {
55
- inspected.component = null;
56
- };
57
-
58
- $scope.toggleEdit = function() {
59
- $scope[$scope.editing ? 'complete' : 'edit']();
60
- };
61
-
42
+ promethee.controller('DetailsController', ['$scope', function($scope) {
62
43
  $scope.options = {
63
44
  toolbar: [
64
45
  ['headline', ['style']],
@@ -1,10 +1,10 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/image">
2
2
  <div
3
3
  ng-controller="ImageController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="promethee-editor__component promethee-editor__component--image"
5
- ng-click="edit($event);"
6
- >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
6
+ >
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Image
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
@@ -36,26 +36,7 @@
36
36
  }
37
37
  });
38
38
 
39
- promethee.controller('ImageController', ['$scope', 'inspected', function($scope, inspected) {
40
- Object.defineProperty($scope, 'editing', {
41
- get: function() {
42
- return inspected.component == $scope.component;
43
- }
44
- });
45
-
46
- $scope.edit = function(event) {
47
- event.stopPropagation();
48
- inspected.component = $scope.component;
49
- };
50
-
51
- $scope.complete = function() {
52
- inspected.component = null;
53
- };
54
-
55
- $scope.toggleEdit = function() {
56
- $scope[$scope.editing ? 'complete' : 'edit']();
57
- };
58
-
39
+ promethee.controller('ImageController', ['$scope', function($scope) {
59
40
  $scope.options = {
60
41
  toolbar: [
61
42
  ['headline', ['style']],
@@ -1,17 +1,17 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/row">
2
2
  <div
3
3
  ng-controller="RowController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="row promethee-editor__component promethee-editor__component--row"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Row
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
11
11
  </div>
12
12
  <ng-include src="'promethee/write/components'"></ng-include>
13
13
  <div class="clearfix"></div>
14
- <span class="btn btn-default btn-block" ng-click="addColumn()" style="margin: 0 6px 6px 6px;width: auto">Add column</span>
14
+ <a ng-click="addColumn()" class="promethee-editor__adder__button"><%= fa_icon :columns, class: "fa-2x" %></a>
15
15
  </div>
16
16
  </div>
17
17
  </script>
@@ -27,28 +27,7 @@
27
27
  }
28
28
  });
29
29
 
30
- promethee.controller('RowController', ['$scope', 'inspected', function($scope, inspected) {
31
- Object.defineProperty($scope, 'editing', {
32
- get: function() {
33
- return inspected.component == $scope.component;
34
- }
35
- });
36
-
37
- $scope.edit = function(event) {
38
- event.stopPropagation();
39
- inspected.component = $scope.component;
40
- };
41
-
42
- $scope.complete = function() {
43
- inspected.component = null;
44
- };
45
-
46
- $scope.toggleEdit = function() {
47
- $scope[$scope.editing ? 'complete' : 'edit']();
48
- };
49
-
50
- $scope.allowedTypes = ['column'];
51
-
30
+ promethee.controller('RowController', ['$scope', function($scope) {
52
31
  $scope.addColumn = function() {
53
32
  this.component.children.push({
54
33
  type: 'column',
@@ -1,10 +1,10 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/slider">
2
2
  <div
3
3
  ng-controller="SliderController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="row promethee-editor__component promethee-editor__component--slider"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Slider
10
10
 
@@ -53,24 +53,7 @@
53
53
  }
54
54
  });
55
55
 
56
- promethee.controller('SliderController', ['$scope', '$element', 'inspected', function($scope, $element, inspected) {
57
- $scope.editing = false;
58
-
59
- $scope.edit = function(event) {
60
- event.stopPropagation();
61
- $scope.editing = true;
62
- inspected.component = $scope.component;
63
- };
64
-
65
- $scope.complete = function() {
66
- $scope.editing = false;
67
- inspected.component = null;
68
- };
69
-
70
- $scope.toggleEdit = function() {
71
- $scope[$scope.editing ? 'complete' : 'edit']();
72
- };
73
-
56
+ promethee.controller('SliderController', ['$scope', '$element', function($scope, $element) {
74
57
  $scope.sliderId = 'slider-' + Date.now() + '-' + Math.floor(Math.random()*1000000);
75
58
 
76
59
  $scope.$watch('editing', function(newVal) {
@@ -1,18 +1,18 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/text">
2
2
  <div
3
3
  ng-controller="TextController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="promethee-editor__component promethee-editor__component--text"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Text
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
11
11
  </div>
12
- <div ng-show="editing">
12
+ <div ng-show="promethee.inspected === component">
13
13
  <summernote config="options" ng-model="component.attributes.body"></summernote>
14
14
  </div>
15
- <div ng-hide="editing">
15
+ <div ng-hide="promethee.inspected === component">
16
16
  <div class="promethee-editor__wrapper" ng-bind-html="component.attributes.body | htmlSafe"></div>
17
17
  </div>
18
18
  </div>
@@ -31,26 +31,7 @@
31
31
  }
32
32
  });
33
33
 
34
- promethee.controller('TextController', ['$scope', 'inspected', function($scope, inspected) {
35
- Object.defineProperty($scope, 'editing', {
36
- get: function() {
37
- return inspected.component == $scope.component;
38
- }
39
- });
40
-
41
- $scope.edit = function(event) {
42
- event.stopPropagation();
43
- inspected.component = $scope.component;
44
- };
45
-
46
- $scope.complete = function() {
47
- inspected.component = null;
48
- };
49
-
50
- $scope.toggleEdit = function() {
51
- $scope[$scope.editing ? 'complete' : 'edit']();
52
- };
53
-
34
+ promethee.controller('TextController', ['$scope', function($scope) {
54
35
  $scope.options = {
55
36
  toolbar: [
56
37
  ['headline', ['style']],
@@ -1,10 +1,10 @@
1
1
  <script type="text/ng-template" id="promethee/write/component/video">
2
2
  <div
3
3
  ng-controller="VideoController"
4
+ ng-click="inspect(component, $event)"
4
5
  class="promethee-editor__component promethee-editor__component--video"
5
- ng-click="edit($event);"
6
6
  >
7
- <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': inspected.component === component}">
7
+ <div class="promethee-editor__component-selected" ng-class="{'promethee-editor__component-selected--visible': promethee.inspected === component}">
8
8
  <div class="promethee-editor__toolbar">
9
9
  Video
10
10
  <ng-include src="'promethee/write/toolbar'"></ng-include>
@@ -12,6 +12,7 @@
12
12
 
13
13
  <div class="embed-responsive embed-responsive-16by9">
14
14
  <iframe ng-if="embed" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen="allowfullscreen" ng-src="{{embed | urlSafe}}"></iframe>
15
+ <div class="video-neutralizing-overlay"></div>
15
16
  </div>
16
17
  </div>
17
18
  </div>
@@ -29,20 +30,20 @@
29
30
  }
30
31
  });
31
32
 
32
- promethee.controller('VideoController', ['$scope', 'inspected', function($scope, inspected) {
33
+ promethee.controller('VideoController', ['$scope', function($scope) {
33
34
  Object.defineProperty($scope, 'editing', {
34
35
  get: function() {
35
- return inspected.component == $scope.component;
36
+ return inspected == $scope.component;
36
37
  }
37
38
  });
38
39
 
39
40
  $scope.edit = function(event) {
40
41
  event.stopPropagation();
41
- inspected.component = $scope.component;
42
+ inspected = $scope.component;
42
43
  };
43
44
 
44
45
  $scope.complete = function() {
45
- inspected.component = null;
46
+ inspected = null;
46
47
  };
47
48
 
48
49
  $scope.toggleEdit = function() {
@@ -2,130 +2,86 @@
2
2
  module Promethee
3
3
  class Data
4
4
  def initialize(data, options = {})
5
- @master_data = convert_if_necessary data
6
- @master_data_unlocalized = deep_clone @master_data
7
- localization_data = options[:localization_data]
8
- unless localization_data.nil?
9
- @localization_data = convert_if_necessary localization_data
10
- localize!
11
- end
12
- end
13
-
14
- def localization_data
15
- prepare_localization
16
- @localization_data
5
+ @master_data = hashify data
6
+ @localization_data_raw = hashify options[:localization_data] if options.include? :localization_data
7
+ prepare_localization_data
8
+ localize_master_data
17
9
  end
18
10
 
19
11
  def localization_data_to_json
20
- localization_data.to_json
12
+ @localization_data.to_json
21
13
  end
22
14
 
23
15
  def to_json
24
16
  @master_data.to_json
25
17
  end
26
18
 
27
- def to_h
28
- @master_data
29
- end
19
+ # The data acts as a hash
20
+ def [](value)
21
+ @master_data[value]
22
+ end
30
23
 
31
24
  protected
32
25
 
33
- def convert_if_necessary(string_or_hash_or_anything)
34
- # string_or_hash_or_anything could be a string a hash or anything so we need to handle potential exception to keep this method safe
35
- begin
36
- converted = string_or_hash_or_anything.is_a?(String) ? JSON.parse(string_or_hash_or_anything, symbolize_names: true) : string_or_hash_or_anything
37
- rescue
38
- converted = {}
26
+ def hashify(string_or_hash)
27
+ string_or_hash.is_a?(String) ? JSON.parse(string_or_hash, symbolize_names: true) : string_or_hash
28
+ end
29
+
30
+ # We merge the localization data in the components from the master data.
31
+ # This will :
32
+ # 1 get what's already localized, based on the component id
33
+ # 2 add new components
34
+ # 3 remove components not in the master anymore
35
+ # 4 take the order from the master
36
+ def prepare_localization_data
37
+ @localization_data = {
38
+ version: @master_data[:version],
39
+ components: []
40
+ }
41
+ # We create a flat list of the children and all their child, in the master's order
42
+ master_components_flat = Promethee::Data.flatten_components @master_data.deep_dup[:children]
43
+ master_components_flat.each do |component|
44
+ localized_component = Promethee::Data.find_localized_component component[:id], @localization_data_raw
45
+ # We take either the localized, or the master component
46
+ data = localized_component || component
47
+ # We add master reference
48
+ data[:master] = component.deep_dup
49
+ # We add it to the list of localized components
50
+ @localization_data[:components] << data
39
51
  end
40
-
41
- # The parsed json could be anything: "\"hey\"" => "hey", "[\"hey\"]" => ["hey"], "null" => nil, ...
42
- converted = {} unless converted.is_a? Hash
43
-
44
- # The data might not be a json string so the "symbolize_names" options wouldn't do anything on a ruby hash because JSON.parse wouldn't be called
45
- converted.deep_symbolize_keys!
46
-
47
- # The children array is required in order to make this class work
48
- converted[:children] = [] unless converted[:children].is_a?(Array)
49
-
50
- # The type is required in order to make this class work
51
- converted[:type] = converted[:type].is_a?(String) || converted[:type].is_a?(Symbol) ? converted[:type].to_s : 'page'
52
-
53
- converted
54
- end
55
-
56
- def deep_clone(hash)
57
- JSON.parse(hash.to_json, symbolize_names: true)
58
- end
59
-
60
- # This will recursively merge the localization_data into the master_data
61
- def localize!
62
- merge!
63
- localize_children!
64
- end
65
-
66
- def merge!
67
- @master_data[:attributes].merge! localized_component[:attributes] if localized?
68
- end
69
-
70
- # Look for localized component with the same id
71
- def localized_component
72
- @localized_component ||= components.select { |component| component[:id] == @master_data[:id] }.first
73
- end
74
-
75
- def components
76
- @components ||= @localization_data.include?(:components) ? @localization_data[:components] : []
77
52
  end
78
53
 
79
- def localized?
80
- !localized_component.nil?
54
+ # Recursively merge the localization_data into the master_data
55
+ def localize_master_data
56
+ Promethee::Data.localize_component @master_data, @localization_data
81
57
  end
82
58
 
83
- def localize_children!
84
- children.each do |child_hash|
85
- child_data = Promethee::Data.new child_hash, localization_data: @localization_data
86
- child_data.localize!
87
- end
59
+ def self.find_localized_component(id, array)
60
+ return nil if array.nil?
61
+ return nil unless array.include? :components
62
+ array[:components].select { |component| component[:id] == id }.first
88
63
  end
89
64
 
90
- def children
91
- return [] if !@master_data.include? :children or @master_data[:children].nil?
92
- @master_data[:children]
65
+ def self.localize_component(component, localization_data)
66
+ localize_component_attributes component, localization_data
67
+ localize_component_children component, localization_data
93
68
  end
94
69
 
95
- def flat_children
96
- self.class.flatten_components @master_data_unlocalized[:children]
70
+ def self.localize_component_attributes(component, localization_data)
71
+ return unless component.include? :attributes
72
+ localized_component = Promethee::Data.find_localized_component component[:id], localization_data
73
+ return if localized_component.nil?
74
+ component[:attributes].merge! localized_component[:attributes]
97
75
  end
98
76
 
99
- def prepare_localization
100
- # TODO extract localizable components from master data, in the correct order, merged with existing ones
101
- # TODO update master_version
102
-
103
- # Selects only text components within the children components flat array
104
- # TODO think about a better way to handle localized attributes among all the components (not only text type)
105
- flat_master_data = flat_children.select { |component| component[:type].to_sym === :text }
106
-
107
- if @localization_data
108
- # If localization_data has been provided, we merge flat_master_data components with its components
109
- @localization_data[:components] = flat_master_data.map do |component|
110
- localized_component = @localization_data[:components].find do |localized_component|
111
- localized_component[:id] == component[:id]
112
- end
113
-
114
- # If the localized_component isn't found, we create it with the master (component)
115
- # Eventually, we add a reference to the master in the localized component
116
- (localized_component || component).merge master: component.deep_dup
117
- end
118
- else
119
- # In the other case, localization_data components are flat_master_data components
120
- @localization_data = {
121
- version: @master_data[:version],
122
- components: flat_master_data
123
- }
124
- end
77
+ def self.localize_component_children(component, localization_data)
78
+ return unless component.include? :children
79
+ component[:children].each { |child| localize_component child, localization_data }
125
80
  end
126
81
 
127
- # This method take an array of components and puts every components and their children into a unique flat array
128
- def self.flatten_components components
82
+ # Takes an array of components and puts every component and their children into a unique flat array
83
+ def self.flatten_components(components)
84
+ return [] if components.nil?
129
85
  components.reduce [] do |flat_components, component|
130
86
  flat_components +
131
87
  [component.except(:children)] +
@@ -133,4 +89,4 @@ module Promethee
133
89
  end
134
90
  end
135
91
  end
136
- end
92
+ end