engine2 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -117
  3. data/{views → app}/app.coffee +0 -0
  4. data/{public/css → app}/app.css +203 -196
  5. data/{views → app}/engine2.coffee +111 -60
  6. data/{views → app}/engine2actions.coffee +47 -28
  7. data/bower.json +13 -0
  8. data/conf/message.yaml +2 -1
  9. data/conf/message_pl.yaml +2 -1
  10. data/config.coffee +62 -0
  11. data/engine2.gemspec +2 -5
  12. data/lib/engine2.rb +1 -1
  13. data/lib/engine2/action.rb +9 -24
  14. data/lib/engine2/core.rb +9 -8
  15. data/lib/engine2/handler.rb +1 -5
  16. data/lib/engine2/meta.rb +61 -24
  17. data/lib/engine2/meta/array_meta.rb +82 -0
  18. data/lib/engine2/meta/decode_meta.rb +1 -1
  19. data/lib/engine2/meta/infra_meta.rb +21 -26
  20. data/lib/engine2/meta/list_meta.rb +2 -3
  21. data/lib/engine2/model.rb +5 -5
  22. data/lib/engine2/pre_bootstrap.rb +9 -1
  23. data/lib/engine2/scheme.rb +4 -0
  24. data/lib/engine2/type_info.rb +8 -1
  25. data/lib/engine2/version.rb +1 -1
  26. data/package.json +38 -0
  27. data/public/img/ajax-loader.gif +0 -0
  28. data/views/fields/blob.slim +4 -5
  29. data/views/fields/bs_select.slim +2 -2
  30. data/views/fields/bsselect_picker.slim +1 -2
  31. data/views/fields/bsselect_picker_opt.slim +1 -2
  32. data/views/fields/checkbox.slim +1 -1
  33. data/views/fields/checkbox_buttons.slim +1 -1
  34. data/views/fields/checkbox_buttons_opt.slim +1 -1
  35. data/views/fields/currency.slim +2 -2
  36. data/views/fields/date.slim +0 -1
  37. data/views/fields/date_range.slim +0 -1
  38. data/views/fields/date_time.slim +0 -1
  39. data/views/fields/datetime.slim +0 -2
  40. data/views/fields/decimal.slim +0 -1
  41. data/views/fields/decimal_date.slim +0 -1
  42. data/views/fields/decimal_time.slim +0 -1
  43. data/views/fields/email.slim +1 -2
  44. data/views/fields/file_store.slim +1 -1
  45. data/views/fields/input_text.slim +1 -2
  46. data/views/fields/integer.slim +1 -1
  47. data/views/fields/list_bsselect.slim +1 -2
  48. data/views/fields/list_bsselect_opt.slim +2 -3
  49. data/views/fields/list_buttons.slim +1 -1
  50. data/views/fields/list_buttons_opt.slim +1 -1
  51. data/views/fields/list_select.slim +1 -2
  52. data/views/fields/list_select_opt.slim +2 -3
  53. data/views/fields/password.slim +1 -2
  54. data/views/fields/radio_checkbox.slim +1 -1
  55. data/views/fields/scaffold.slim +1 -1
  56. data/views/fields/scaffold_picker.slim +1 -2
  57. data/views/fields/select_picker.slim +1 -2
  58. data/views/fields/select_picker_opt.slim +1 -2
  59. data/views/fields/text_area.slim +0 -1
  60. data/views/fields/time.slim +0 -1
  61. data/views/fields/typeahead_picker.slim +1 -2
  62. data/views/index.slim +4 -8
  63. data/views/scaffold/fields.slim +4 -4
  64. data/views/scaffold/form.slim +1 -7
  65. data/views/scaffold/form_collapse.slim +19 -0
  66. data/views/scaffold/form_tabs.slim +9 -0
  67. data/views/scaffold/list.slim +1 -12
  68. data/views/scaffold/search.slim +2 -9
  69. data/views/scaffold/search_collapse.slim +26 -0
  70. data/views/scaffold/search_tabs.slim +15 -0
  71. data/views/scaffold/view.slim +5 -15
  72. data/views/scaffold/view_collapse.slim +22 -0
  73. data/views/scaffold/view_tabs.slim +11 -0
  74. data/views/search_fields/bsmselect_picker.slim +1 -2
  75. data/views/search_fields/bsselect_picker.slim +1 -2
  76. data/views/search_fields/checkbox.slim +3 -3
  77. data/views/search_fields/checkbox2.slim +5 -5
  78. data/views/search_fields/checkbox_buttons.slim +2 -2
  79. data/views/search_fields/date_range.slim +0 -2
  80. data/views/search_fields/decimal_date_range.slim +5 -5
  81. data/views/search_fields/input_text.slim +2 -2
  82. data/views/search_fields/integer.slim +1 -1
  83. data/views/search_fields/integer_range.slim +2 -2
  84. data/views/search_fields/list_bsmselect.slim +1 -1
  85. data/views/search_fields/list_bsselect.slim +0 -1
  86. data/views/search_fields/list_buttons.slim +2 -2
  87. data/views/search_fields/list_select.slim +1 -2
  88. data/views/search_fields/scaffold_picker.slim +1 -2
  89. data/views/search_fields/select_picker.slim +1 -2
  90. data/views/search_fields/typeahead_picker.slim +1 -2
  91. metadata +29 -103
  92. data/public/assets/javascripts.js +0 -13
  93. data/public/assets/styles.css +0 -4
  94. data/public/css/angular-motion.css +0 -1022
  95. data/public/css/angular-ui-tree.min.css +0 -1
  96. data/public/css/bootstrap-additions.css +0 -1560
  97. data/public/css/bootstrap.min.css +0 -11
  98. data/public/css/font-awesome.min.css +0 -4
  99. data/public/fonts/FontAwesome.otf +0 -0
  100. data/public/fonts/fontawesome-webfont.eot +0 -0
  101. data/public/fonts/fontawesome-webfont.svg +0 -655
  102. data/public/fonts/fontawesome-webfont.ttf +0 -0
  103. data/public/fonts/fontawesome-webfont.woff +0 -0
  104. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  105. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  106. data/public/fonts/glyphicons-halflings-regular.svg +0 -288
  107. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  108. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  109. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  110. data/public/images/file.png +0 -0
  111. data/public/images/folder-closed.png +0 -0
  112. data/public/images/folder.png +0 -0
  113. data/public/images/node-closed-2.png +0 -0
  114. data/public/images/node-closed-light.png +0 -0
  115. data/public/images/node-closed.png +0 -0
  116. data/public/images/node-opened-2.png +0 -0
  117. data/public/images/node-opened-light.png +0 -0
  118. data/public/images/node-opened.png +0 -0
  119. data/public/img/ajax-loader-light.gif +0 -0
  120. data/public/js/angular-animate.js +0 -4147
  121. data/public/js/angular-cookies.js +0 -322
  122. data/public/js/angular-local-storage.js +0 -455
  123. data/public/js/angular-route.js +0 -1025
  124. data/public/js/angular-sanitize.js +0 -717
  125. data/public/js/angular-strap.js +0 -4339
  126. data/public/js/angular-strap.tpl.js +0 -43
  127. data/public/js/angular-ui-tree.js +0 -1569
  128. data/public/js/angular.js +0 -30868
  129. data/public/js/i18n/angular-locale_pl.js +0 -115
  130. data/public/js/lodash.custom.min.js +0 -99
  131. data/public/js/ng-file-upload-shim.min.js +0 -2
  132. data/public/js/ng-file-upload.min.js +0 -3
  133. data/views/engine2templates.coffee +0 -0
@@ -1,43 +0,0 @@
1
- /**
2
- * angular-strap
3
- * @version v2.3.8 - 2016-03-31
4
- * @link http://mgcrea.github.io/angular-strap
5
- * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
6
- * @license MIT License, http://www.opensource.org/licenses/MIT
7
- */
8
- (function(window, document, undefined) {
9
- 'use strict';
10
- angular.module('mgcrea.ngStrap.aside').run([ '$templateCache', function($templateCache) {
11
- $templateCache.put('aside/aside.tpl.html', '<div class="aside" tabindex="-1" role="dialog"><div class="aside-dialog"><div class="aside-content"><div class="aside-header" ng-show="title"><button type="button" class="close" ng-click="$hide()">&times;</button><h4 class="aside-title" ng-bind="title"></h4></div><div class="aside-body" ng-bind="content"></div><div class="aside-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>');
12
- } ]);
13
- angular.module('mgcrea.ngStrap.alert').run([ '$templateCache', function($templateCache) {
14
- $templateCache.put('alert/alert.tpl.html', '<div class="alert" ng-class="[type ? \'alert-\' + type : null]"><button type="button" class="close" ng-if="dismissable" ng-click="$hide()">&times;</button> <strong ng-bind="title"></strong>&nbsp;<span ng-bind-html="content"></span></div>');
15
- } ]);
16
- angular.module('mgcrea.ngStrap.datepicker').run([ '$templateCache', function($templateCache) {
17
- $templateCache.put('datepicker/datepicker.tpl.html', '<div class="dropdown-menu datepicker" ng-class="\'datepicker-mode-\' + $mode" style="max-width: 320px"><table style="table-layout: fixed; height: 100%; width: 100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$selectPane(-1)"><i class="{{$iconLeft}}"></i></button></th><th colspan="{{ rows[0].length - 2 }}"><button tabindex="-1" type="button" class="btn btn-default btn-block text-strong" ng-click="$toggleMode()"><strong style="text-transform: capitalize" ng-bind="title"></strong></button></th><th><button tabindex="-1" type="button" class="btn btn-default pull-right" ng-click="$selectPane(+1)"><i class="{{$iconRight}}"></i></button></th></tr><tr ng-if="showLabels" ng-bind-html="labels"></tr></thead><tbody><tr ng-repeat="(i, row) in rows" height="{{ 100 / rows.length }}%"><td class="text-center" ng-repeat="(j, el) in row"><button tabindex="-1" type="button" class="btn btn-default" style="width: 100%" ng-class="{\'btn-primary\': el.selected, \'btn-info btn-today\': el.isToday && !el.selected}" ng-click="$select(el.date)" ng-disabled="el.disabled"><span ng-class="{\'text-muted\': el.muted}" ng-bind="el.label"></span></button></td></tr></tbody></table></div>');
18
- } ]);
19
- angular.module('mgcrea.ngStrap.modal').run([ '$templateCache', function($templateCache) {
20
- $templateCache.put('modal/modal.tpl.html', '<div class="modal" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header" ng-show="title"><button type="button" class="close" aria-label="Close" ng-click="$hide()"><span aria-hidden="true">&times;</span></button><h4 class="modal-title" ng-bind="title"></h4></div><div class="modal-body" ng-bind="content"></div><div class="modal-footer"><button type="button" class="btn btn-default" ng-click="$hide()">Close</button></div></div></div></div>');
21
- } ]);
22
- angular.module('mgcrea.ngStrap.dropdown').run([ '$templateCache', function($templateCache) {
23
- $templateCache.put('dropdown/dropdown.tpl.html', '<ul tabindex="-1" class="dropdown-menu" role="menu" ng-show="content && content.length"><li role="presentation" ng-class="{divider: item.divider, active: item.active}" ng-repeat="item in content"><a role="menuitem" tabindex="-1" ng-href="{{item.href}}" ng-if="!item.divider && item.href" target="{{item.target || \'\'}}" ng-bind="item.text"></a> <a role="menuitem" tabindex="-1" href="javascript:void(0)" ng-if="!item.divider && item.click" ng-click="$eval(item.click);$hide()" ng-bind="item.text"></a></li></ul>');
24
- } ]);
25
- angular.module('mgcrea.ngStrap.popover').run([ '$templateCache', function($templateCache) {
26
- $templateCache.put('popover/popover.tpl.html', '<div class="popover" tabindex="-1"><div class="arrow"></div><h3 class="popover-title" ng-bind="title" ng-show="title"></h3><div class="popover-content" ng-bind="content"></div></div>');
27
- } ]);
28
- angular.module('mgcrea.ngStrap.select').run([ '$templateCache', function($templateCache) {
29
- $templateCache.put('select/select.tpl.html', '<ul tabindex="-1" class="select dropdown-menu" ng-show="$isVisible()" role="select"><li ng-if="$showAllNoneButtons"><div class="btn-group" style="margin-bottom: 5px; margin-left: 5px"><button type="button" class="btn btn-default btn-xs" ng-click="$selectAll()">{{$allText}}</button> <button type="button" class="btn btn-default btn-xs" ng-click="$selectNone()">{{$noneText}}</button></div></li><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $isActive($index)}"><a style="cursor: default" role="menuitem" tabindex="-1" ng-click="$select($index, $event)"><i class="{{$iconCheckmark}} pull-right" ng-if="$isMultiple && $isActive($index)"></i> <span ng-bind="match.label"></span></a></li></ul>');
30
- } ]);
31
- angular.module('mgcrea.ngStrap.tab').run([ '$templateCache', function($templateCache) {
32
- $templateCache.put('tab/tab.tpl.html', '<ul class="nav" ng-class="$navClass" role="tablist"><li role="presentation" ng-repeat="$pane in $panes track by $index" ng-class="[ $isActive($pane, $index) ? $activeClass : \'\', $pane.disabled ? \'disabled\' : \'\' ]"><a role="tab" data-toggle="tab" ng-click="!$pane.disabled && $setActive($pane.name || $index)" data-index="{{ $index }}" ng-bind-html="$pane.title" aria-controls="$pane.title"></a></li></ul><div ng-transclude class="tab-content"></div>');
33
- } ]);
34
- angular.module('mgcrea.ngStrap.timepicker').run([ '$templateCache', function($templateCache) {
35
- $templateCache.put('timepicker/timepicker.tpl.html', '<div class="dropdown-menu timepicker" style="min-width: 0px;width: auto"><table height="100%"><thead><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 0)"><i class="{{ $iconUp }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 1)"><i class="{{ $iconUp }}"></i></button></th><th ng-if="showSeconds">&nbsp;</th><th ng-if="showSeconds"><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 2)"><i class="{{ $iconUp }}"></i></button></th></tr></thead><tbody><tr ng-repeat="(i, row) in rows"><td class="text-center"><button tabindex="-1" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[0].selected}" ng-click="$select(row[0].date, 0)" ng-disabled="row[0].disabled"><span ng-class="{\'text-muted\': row[0].muted}" ng-bind="row[0].label"></span></button></td><td><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td class="text-center"><button tabindex="-1" ng-if="row[1].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[1].selected}" ng-click="$select(row[1].date, 1)" ng-disabled="row[1].disabled"><span ng-class="{\'text-muted\': row[1].muted}" ng-bind="row[1].label"></span></button></td><td ng-if="showSeconds"><span ng-bind="i == midIndex ? timeSeparator : \' \'"></span></td><td ng-if="showSeconds" class="text-center"><button tabindex="-1" ng-if="row[2].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{\'btn-primary\': row[2].selected}" ng-click="$select(row[2].date, 2)" ng-disabled="row[2].disabled"><span ng-class="{\'text-muted\': row[2].muted}" ng-bind="row[2].label"></span></button></td><td ng-if="showAM">&nbsp;</td><td ng-if="showAM"><button tabindex="-1" ng-show="i == midIndex - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !!isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">AM</button> <button tabindex="-1" ng-show="i == midIndex + 1 - !isAM * 1" style="width: 100%" type="button" ng-class="{\'btn-primary\': !isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">PM</button></td></tr></tbody><tfoot><tr class="text-center"><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 0)"><i class="{{ $iconDown }}"></i></button></th><th>&nbsp;</th><th><button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 1)"><i class="{{ $iconDown }}"></i></button></th><th ng-if="showSeconds">&nbsp;</th><th ng-if="showSeconds"><button ng-if="showSeconds" tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 2)"><i class="{{ $iconDown }}"></i></button></th></tr></tfoot></table></div>');
36
- } ]);
37
- angular.module('mgcrea.ngStrap.typeahead').run([ '$templateCache', function($templateCache) {
38
- $templateCache.put('typeahead/typeahead.tpl.html', '<ul tabindex="-1" class="typeahead dropdown-menu" ng-show="$isVisible()" role="select"><li role="presentation" ng-repeat="match in $matches" ng-class="{active: $index == $activeIndex}"><a role="menuitem" tabindex="-1" ng-click="$select($index, $event)" ng-bind="match.label"></a></li></ul>');
39
- } ]);
40
- angular.module('mgcrea.ngStrap.tooltip').run([ '$templateCache', function($templateCache) {
41
- $templateCache.put('tooltip/tooltip.tpl.html', '<div class="tooltip in" ng-show="title"><div class="tooltip-arrow"></div><div class="tooltip-inner" ng-bind="title"></div></div>');
42
- } ]);
43
- })(window, document);
@@ -1,1569 +0,0 @@
1
- /**
2
- * @license Angular UI Tree v2.15.0
3
- * (c) 2010-2016. https://github.com/angular-ui-tree/angular-ui-tree
4
- * License: MIT
5
- */
6
- (function () {
7
- 'use strict';
8
-
9
- angular.module('ui.tree', [])
10
- .constant('treeConfig', {
11
- treeClass: 'angular-ui-tree',
12
- emptyTreeClass: 'angular-ui-tree-empty',
13
- hiddenClass: 'angular-ui-tree-hidden',
14
- nodesClass: 'angular-ui-tree-nodes',
15
- nodeClass: 'angular-ui-tree-node',
16
- handleClass: 'angular-ui-tree-handle',
17
- placeholderClass: 'angular-ui-tree-placeholder',
18
- dragClass: 'angular-ui-tree-drag',
19
- dragThreshold: 3,
20
- levelThreshold: 30,
21
- defaultCollapsed: false
22
- });
23
-
24
- })();
25
-
26
- (function () {
27
- 'use strict';
28
-
29
- angular.module('ui.tree')
30
-
31
- .controller('TreeHandleController', ['$scope', '$element',
32
- function ($scope, $element) {
33
- this.scope = $scope;
34
-
35
- $scope.$element = $element;
36
- $scope.$nodeScope = null;
37
- $scope.$type = 'uiTreeHandle';
38
-
39
- }
40
- ]);
41
- })();
42
-
43
- (function () {
44
- 'use strict';
45
-
46
- angular.module('ui.tree')
47
- .controller('TreeNodeController', ['$scope', '$element',
48
- function ($scope, $element) {
49
- this.scope = $scope;
50
-
51
- $scope.$element = $element;
52
- $scope.$modelValue = null; // Model value for node;
53
- $scope.$parentNodeScope = null; // uiTreeNode Scope of parent node;
54
- $scope.$childNodesScope = null; // uiTreeNodes Scope of child nodes.
55
- $scope.$parentNodesScope = null; // uiTreeNodes Scope of parent nodes.
56
- $scope.$treeScope = null; // uiTree scope
57
- $scope.$handleScope = null; // it's handle scope
58
- $scope.$type = 'uiTreeNode';
59
- $scope.$$allowNodeDrop = false;
60
- $scope.collapsed = false;
61
-
62
- $scope.init = function (controllersArr) {
63
- var treeNodesCtrl = controllersArr[0];
64
- $scope.$treeScope = controllersArr[1] ? controllersArr[1].scope : null;
65
-
66
- // find the scope of it's parent node
67
- $scope.$parentNodeScope = treeNodesCtrl.scope.$nodeScope;
68
- // modelValue for current node
69
- $scope.$modelValue = treeNodesCtrl.scope.$modelValue[$scope.$index];
70
- $scope.$parentNodesScope = treeNodesCtrl.scope;
71
- treeNodesCtrl.scope.initSubNode($scope); // init sub nodes
72
-
73
- $element.on('$destroy', function () {
74
- treeNodesCtrl.scope.destroySubNode($scope); // destroy sub nodes
75
- });
76
- };
77
-
78
- $scope.index = function () {
79
- return $scope.$parentNodesScope.$modelValue.indexOf($scope.$modelValue);
80
- };
81
-
82
- $scope.dragEnabled = function () {
83
- return !($scope.$treeScope && !$scope.$treeScope.dragEnabled);
84
- };
85
-
86
- $scope.isSibling = function (targetNode) {
87
- return $scope.$parentNodesScope == targetNode.$parentNodesScope;
88
- };
89
-
90
- $scope.isChild = function (targetNode) {
91
- var nodes = $scope.childNodes();
92
- return nodes && nodes.indexOf(targetNode) > -1;
93
- };
94
-
95
- $scope.prev = function () {
96
- var index = $scope.index();
97
- if (index > 0) {
98
- return $scope.siblings()[index - 1];
99
- }
100
- return null;
101
- };
102
-
103
- $scope.siblings = function () {
104
- return $scope.$parentNodesScope.childNodes();
105
- };
106
-
107
- $scope.childNodesCount = function () {
108
- return $scope.childNodes() ? $scope.childNodes().length : 0;
109
- };
110
-
111
- $scope.hasChild = function () {
112
- return $scope.childNodesCount() > 0;
113
- };
114
-
115
- $scope.childNodes = function () {
116
- return $scope.$childNodesScope && $scope.$childNodesScope.$modelValue ?
117
- $scope.$childNodesScope.childNodes() :
118
- null;
119
- };
120
-
121
- $scope.accept = function (sourceNode, destIndex) {
122
- return $scope.$childNodesScope &&
123
- $scope.$childNodesScope.$modelValue &&
124
- $scope.$childNodesScope.accept(sourceNode, destIndex);
125
- };
126
-
127
- $scope.remove = function () {
128
- return $scope.$parentNodesScope.removeNode($scope);
129
- };
130
-
131
- $scope.toggle = function () {
132
- $scope.collapsed = !$scope.collapsed;
133
- };
134
-
135
- $scope.collapse = function () {
136
- $scope.collapsed = true;
137
- };
138
-
139
- $scope.expand = function () {
140
- $scope.collapsed = false;
141
- };
142
-
143
- $scope.depth = function () {
144
- var parentNode = $scope.$parentNodeScope;
145
- if (parentNode) {
146
- return parentNode.depth() + 1;
147
- }
148
- return 1;
149
- };
150
-
151
- /**
152
- * Returns the depth of the deepest subtree under this node
153
- * @param scope a TreeNodesController scope object
154
- * @returns Depth of all nodes *beneath* this node. If scope belongs to a leaf node, the
155
- * result is 0 (it has no subtree).
156
- */
157
- function countSubTreeDepth(scope) {
158
- var thisLevelDepth = 0,
159
- childNodes = scope.childNodes(),
160
- childNode,
161
- childDepth,
162
- i;
163
- if (!childNodes || childNodes.length === 0) {
164
- return 0;
165
- }
166
- for (i = childNodes.length - 1; i >= 0 ; i--) {
167
- childNode = childNodes[i],
168
- childDepth = 1 + countSubTreeDepth(childNode);
169
- thisLevelDepth = Math.max(thisLevelDepth, childDepth);
170
- }
171
- return thisLevelDepth;
172
- }
173
-
174
- $scope.maxSubDepth = function () {
175
- return $scope.$childNodesScope ? countSubTreeDepth($scope.$childNodesScope) : 0;
176
- };
177
- }
178
- ]);
179
- })();
180
-
181
- (function () {
182
- 'use strict';
183
-
184
- angular.module('ui.tree')
185
-
186
- .controller('TreeNodesController', ['$scope', '$element',
187
- function ($scope, $element) {
188
- this.scope = $scope;
189
-
190
- $scope.$element = $element;
191
- $scope.$modelValue = null;
192
- $scope.$nodeScope = null; // the scope of node which the nodes belongs to
193
- $scope.$treeScope = null;
194
- $scope.$type = 'uiTreeNodes';
195
- $scope.$nodesMap = {};
196
-
197
- $scope.nodropEnabled = false;
198
- $scope.maxDepth = 0;
199
- $scope.cloneEnabled = false;
200
-
201
- $scope.initSubNode = function (subNode) {
202
- if (!subNode.$modelValue) {
203
- return null;
204
- }
205
- $scope.$nodesMap[subNode.$modelValue.$$hashKey] = subNode;
206
- };
207
-
208
- $scope.destroySubNode = function (subNode) {
209
- if (!subNode.$modelValue) {
210
- return null;
211
- }
212
- $scope.$nodesMap[subNode.$modelValue.$$hashKey] = null;
213
- };
214
-
215
- $scope.accept = function (sourceNode, destIndex) {
216
- return $scope.$treeScope.$callbacks.accept(sourceNode, $scope, destIndex);
217
- };
218
-
219
- $scope.beforeDrag = function (sourceNode) {
220
- return $scope.$treeScope.$callbacks.beforeDrag(sourceNode);
221
- };
222
-
223
- $scope.isParent = function (node) {
224
- return node.$parentNodesScope == $scope;
225
- };
226
-
227
- $scope.hasChild = function () {
228
- return $scope.$modelValue.length > 0;
229
- };
230
-
231
- $scope.safeApply = function (fn) {
232
- var phase = this.$root.$$phase;
233
- if (phase == '$apply' || phase == '$digest') {
234
- if (fn && (typeof (fn) === 'function')) {
235
- fn();
236
- }
237
- } else {
238
- this.$apply(fn);
239
- }
240
- };
241
-
242
- $scope.removeNode = function (node) {
243
- var index = $scope.$modelValue.indexOf(node.$modelValue);
244
- if (index > -1) {
245
- $scope.safeApply(function () {
246
- $scope.$modelValue.splice(index, 1)[0];
247
- });
248
- return $scope.$treeScope.$callbacks.removed(node);
249
- }
250
- return null;
251
- };
252
-
253
- $scope.insertNode = function (index, nodeData) {
254
- $scope.safeApply(function () {
255
- $scope.$modelValue.splice(index, 0, nodeData);
256
- });
257
- };
258
-
259
- $scope.childNodes = function () {
260
- var i, nodes = [];
261
- if ($scope.$modelValue) {
262
- for (i = 0; i < $scope.$modelValue.length; i++) {
263
- nodes.push($scope.$nodesMap[$scope.$modelValue[i].$$hashKey]);
264
- }
265
- }
266
- return nodes;
267
- };
268
-
269
- $scope.depth = function () {
270
- if ($scope.$nodeScope) {
271
- return $scope.$nodeScope.depth();
272
- }
273
- return 0; // if it has no $nodeScope, it's root
274
- };
275
-
276
- // check if depth limit has reached
277
- $scope.outOfDepth = function (sourceNode) {
278
- var maxDepth = $scope.maxDepth || $scope.$treeScope.maxDepth;
279
- if (maxDepth > 0) {
280
- return $scope.depth() + sourceNode.maxSubDepth() + 1 > maxDepth;
281
- }
282
- return false;
283
- };
284
-
285
- }
286
- ]);
287
- })();
288
-
289
- (function () {
290
- 'use strict';
291
-
292
- angular.module('ui.tree')
293
-
294
- .controller('TreeController', ['$scope', '$element',
295
- function ($scope, $element) {
296
- this.scope = $scope;
297
-
298
- $scope.$element = $element;
299
- $scope.$nodesScope = null; // root nodes
300
- $scope.$type = 'uiTree';
301
- $scope.$emptyElm = null;
302
- $scope.$callbacks = null;
303
-
304
- $scope.dragEnabled = true;
305
- $scope.emptyPlaceholderEnabled = true;
306
- $scope.maxDepth = 0;
307
- $scope.dragDelay = 0;
308
- $scope.cloneEnabled = false;
309
- $scope.nodropEnabled = false;
310
-
311
- // Check if it's a empty tree
312
- $scope.isEmpty = function () {
313
- return ($scope.$nodesScope && $scope.$nodesScope.$modelValue
314
- && $scope.$nodesScope.$modelValue.length === 0);
315
- };
316
-
317
- // add placeholder to empty tree
318
- $scope.place = function (placeElm) {
319
- $scope.$nodesScope.$element.append(placeElm);
320
- $scope.$emptyElm.remove();
321
- };
322
-
323
- this.resetEmptyElement = function () {
324
- if ((!$scope.$nodesScope.$modelValue || $scope.$nodesScope.$modelValue.length === 0) &&
325
- $scope.emptyPlaceholderEnabled) {
326
- $element.append($scope.$emptyElm);
327
- } else {
328
- $scope.$emptyElm.remove();
329
- }
330
- };
331
-
332
- $scope.resetEmptyElement = this.resetEmptyElement;
333
- }
334
- ]);
335
- })();
336
-
337
- (function () {
338
- 'use strict';
339
-
340
- angular.module('ui.tree')
341
- .directive('uiTree', ['treeConfig', '$window',
342
- function (treeConfig, $window) {
343
- return {
344
- restrict: 'A',
345
- scope: true,
346
- controller: 'TreeController',
347
- link: function (scope, element, attrs, ctrl) {
348
- var callbacks = {
349
- accept: null,
350
- beforeDrag: null
351
- },
352
- config = {},
353
- tdElm,
354
- $trElm,
355
- emptyElmColspan;
356
-
357
- angular.extend(config, treeConfig);
358
- if (config.treeClass) {
359
- element.addClass(config.treeClass);
360
- }
361
-
362
- if (element.prop('tagName').toLowerCase() === 'table') {
363
- scope.$emptyElm = angular.element($window.document.createElement('tr'));
364
- $trElm = element.find('tr');
365
- // If we can find a tr, then we can use its td children as the empty element colspan.
366
- if ($trElm.length > 0) {
367
- emptyElmColspan = angular.element($trElm).children().length;
368
- } else {
369
- // If not, by setting a huge colspan we make sure it takes full width.
370
- emptyElmColspan = 1000000;
371
- }
372
- tdElm = angular.element($window.document.createElement('td'))
373
- .attr('colspan', emptyElmColspan);
374
- scope.$emptyElm.append(tdElm);
375
- } else {
376
- scope.$emptyElm = angular.element($window.document.createElement('div'));
377
- }
378
-
379
- if (config.emptyTreeClass) {
380
- scope.$emptyElm.addClass(config.emptyTreeClass);
381
- }
382
-
383
- scope.$watch('$nodesScope.$modelValue.length', function (val) {
384
- if (!angular.isNumber(val)) {
385
- return;
386
- }
387
-
388
- ctrl.resetEmptyElement();
389
- }, true);
390
-
391
- scope.$watch(attrs.dragEnabled, function (val) {
392
- if ((typeof val) == 'boolean') {
393
- scope.dragEnabled = val;
394
- }
395
- });
396
-
397
- scope.$watch(attrs.emptyPlaceholderEnabled, function (val) {
398
- if ((typeof val) == 'boolean') {
399
- scope.emptyPlaceholderEnabled = val;
400
- ctrl.resetEmptyElement();
401
- }
402
- });
403
-
404
- scope.$watch(attrs.nodropEnabled, function (val) {
405
- if ((typeof val) == 'boolean') {
406
- scope.nodropEnabled = val;
407
- }
408
- });
409
-
410
- scope.$watch(attrs.cloneEnabled, function (val) {
411
- if ((typeof val) == 'boolean') {
412
- scope.cloneEnabled = val;
413
- }
414
- });
415
-
416
- scope.$watch(attrs.maxDepth, function (val) {
417
- if ((typeof val) == 'number') {
418
- scope.maxDepth = val;
419
- }
420
- });
421
-
422
- scope.$watch(attrs.dragDelay, function (val) {
423
- if ((typeof val) == 'number') {
424
- scope.dragDelay = val;
425
- }
426
- });
427
-
428
- /**
429
- * Callback checks if the destination node can accept the dragged node.
430
- * By default, ui-tree will check that 'data-nodrop-enabled' is not set for the
431
- * destination ui-tree-nodes, and that the 'max-depth' attribute will not be exceeded
432
- * if it is set on the ui-tree or ui-tree-nodes.
433
- * This callback can be overridden, but callers must manually enforce nodrop and max-depth
434
- * themselves if they need those to be enforced.
435
- * @param sourceNodeScope Scope of the ui-tree-node being dragged
436
- * @param destNodesScope Scope of the ui-tree-nodes where the node is hovering
437
- * @param destIndex Index in the destination nodes array where the source node will drop
438
- * @returns {boolean} True if the node is permitted to be dropped here
439
- */
440
- callbacks.accept = function (sourceNodeScope, destNodesScope, destIndex) {
441
- return !(destNodesScope.nodropEnabled || destNodesScope.$treeScope.nodropEnabled || destNodesScope.outOfDepth(sourceNodeScope));
442
- };
443
-
444
- callbacks.beforeDrag = function (sourceNodeScope) {
445
- return true;
446
- };
447
-
448
- callbacks.removed = function (node) {
449
-
450
- };
451
-
452
- /**
453
- * Callback is fired when a node is successfully dropped in a new location
454
- * @param event
455
- */
456
- callbacks.dropped = function (event) {
457
-
458
- };
459
-
460
- /**
461
- * Callback is fired each time the user starts dragging a node
462
- * @param event
463
- */
464
- callbacks.dragStart = function (event) {
465
-
466
- };
467
-
468
- /**
469
- * Callback is fired each time a dragged node is moved with the mouse/touch.
470
- * @param event
471
- */
472
- callbacks.dragMove = function (event) {
473
-
474
- };
475
-
476
- /**
477
- * Callback is fired when the tree exits drag mode. If the user dropped a node, the drop may have been
478
- * accepted or reverted.
479
- * @param event
480
- */
481
- callbacks.dragStop = function (event) {
482
-
483
- };
484
-
485
- /**
486
- * Callback is fired when a user drops a node (but prior to processing the drop action)
487
- * beforeDrop can return a Promise, truthy, or falsy (returning nothing is falsy).
488
- * If it returns falsy, or a resolve Promise, the node move is accepted
489
- * If it returns truthy, or a rejected Promise, the node move is reverted
490
- * @param event
491
- * @returns {Boolean|Promise} Truthy (or rejected Promise) to cancel node move; falsy (or resolved promise)
492
- */
493
- callbacks.beforeDrop = function (event) {
494
-
495
- };
496
-
497
- scope.$watch(attrs.uiTree, function (newVal, oldVal) {
498
- angular.forEach(newVal, function (value, key) {
499
- if (callbacks[key]) {
500
- if (typeof value === 'function') {
501
- callbacks[key] = value;
502
- }
503
- }
504
- });
505
-
506
- scope.$callbacks = callbacks;
507
- }, true);
508
-
509
-
510
- }
511
- };
512
- }
513
- ]);
514
- })();
515
-
516
- (function () {
517
- 'use strict';
518
-
519
- angular.module('ui.tree')
520
- .directive('uiTreeHandle', ['treeConfig',
521
- function (treeConfig) {
522
- return {
523
- require: '^uiTreeNode',
524
- restrict: 'A',
525
- scope: true,
526
- controller: 'TreeHandleController',
527
- link: function (scope, element, attrs, treeNodeCtrl) {
528
- var config = {};
529
- angular.extend(config, treeConfig);
530
- if (config.handleClass) {
531
- element.addClass(config.handleClass);
532
- }
533
- // connect with the tree node.
534
- if (scope != treeNodeCtrl.scope) {
535
- scope.$nodeScope = treeNodeCtrl.scope;
536
- treeNodeCtrl.scope.$handleScope = scope;
537
- }
538
- }
539
- };
540
- }
541
- ]);
542
- })();
543
-
544
- (function () {
545
- 'use strict';
546
-
547
- angular.module('ui.tree')
548
-
549
- .directive('uiTreeNode', ['treeConfig', 'UiTreeHelper', '$window', '$document', '$timeout', '$q', '$rootElement',
550
- function (treeConfig, UiTreeHelper, $window, $document, $timeout, $q, $rootElement) {
551
- return {
552
- require: ['^uiTreeNodes', '^uiTree'],
553
- restrict: 'A',
554
- controller: 'TreeNodeController',
555
- link: function (scope, element, attrs, controllersArr) {
556
- // todo startPos is unused
557
- var config = {},
558
- hasTouch = 'ontouchstart' in window,
559
- startPos, firstMoving, dragInfo, pos,
560
- placeElm, hiddenPlaceElm, dragElm,
561
- treeScope = null,
562
- elements, // As a parameter for callbacks
563
- dragDelaying = true,
564
- dragStarted = false,
565
- dragTimer = null,
566
- body = document.body,
567
- html = document.documentElement,
568
- document_height,
569
- document_width,
570
- dragStart,
571
- tagName,
572
- dragMove,
573
- dragEnd,
574
- dragStartEvent,
575
- dragMoveEvent,
576
- dragEndEvent,
577
- dragCancelEvent,
578
- dragDelay,
579
- bindDragStartEvents,
580
- bindDragMoveEvents,
581
- unbindDragMoveEvents,
582
- keydownHandler,
583
- outOfBounds,
584
- isHandleChild,
585
- el;
586
-
587
- angular.extend(config, treeConfig);
588
- if (config.nodeClass) {
589
- element.addClass(config.nodeClass);
590
- }
591
- scope.init(controllersArr);
592
-
593
- scope.collapsed = !!UiTreeHelper.getNodeAttribute(scope, 'collapsed') || treeConfig.defaultCollapsed;
594
- scope.sourceOnly = scope.nodropEnabled || scope.$treeScope.nodropEnabled;
595
-
596
- scope.$watch(attrs.collapsed, function (val) {
597
- if ((typeof val) == 'boolean') {
598
- scope.collapsed = val;
599
- }
600
- });
601
-
602
- scope.$watch('collapsed', function (val) {
603
- UiTreeHelper.setNodeAttribute(scope, 'collapsed', val);
604
- attrs.$set('collapsed', val);
605
- });
606
-
607
- scope.$on('angular-ui-tree:collapse-all', function () {
608
- scope.collapsed = true;
609
- });
610
-
611
- scope.$on('angular-ui-tree:expand-all', function () {
612
- scope.collapsed = false;
613
- });
614
-
615
- /**
616
- * Called when the user has grabbed a node and started dragging it
617
- * @param e
618
- */
619
- dragStart = function (e) {
620
- // disable right click
621
- if (!hasTouch && (e.button === 2 || e.which === 3)) {
622
- return;
623
- }
624
-
625
- // event has already fired in other scope
626
- if (e.uiTreeDragging || (e.originalEvent && e.originalEvent.uiTreeDragging)) {
627
- return;
628
- }
629
-
630
- // the node being dragged
631
- var eventElm = angular.element(e.target),
632
- isHandleChild, cloneElm, eventElmTagName, tagName,
633
- eventObj, tdElm, hStyle,
634
- isTreeNode,
635
- isTreeNodeHandle;
636
-
637
- // if the target element is a child element of a ui-tree-handle,
638
- // use the containing handle element as target element
639
- isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(eventElm);
640
- if (isHandleChild) {
641
- eventElm = angular.element(isHandleChild);
642
- }
643
-
644
- cloneElm = element.clone();
645
- isTreeNode = UiTreeHelper.elementIsTreeNode(eventElm);
646
- isTreeNodeHandle = UiTreeHelper.elementIsTreeNodeHandle(eventElm);
647
-
648
- if (!isTreeNode && !isTreeNodeHandle) {
649
- return;
650
- }
651
-
652
- if (isTreeNode && UiTreeHelper.elementContainsTreeNodeHandler(eventElm)) {
653
- return;
654
- }
655
-
656
- eventElmTagName = eventElm.prop('tagName').toLowerCase();
657
- if (eventElmTagName == 'input' ||
658
- eventElmTagName == 'textarea' ||
659
- eventElmTagName == 'button' ||
660
- eventElmTagName == 'select') { // if it's a input or button, ignore it
661
- return;
662
- }
663
-
664
- // check if it or it's parents has a 'data-nodrag' attribute
665
- el = angular.element(e.target);
666
- while (el && el[0] && el[0] !== element) {
667
- if (UiTreeHelper.nodrag(el)) { // if the node mark as `nodrag`, DONOT drag it.
668
- return;
669
- }
670
- el = el.parent();
671
- }
672
-
673
- if (!scope.beforeDrag(scope)) {
674
- return;
675
- }
676
-
677
- e.uiTreeDragging = true; // stop event bubbling
678
- if (e.originalEvent) {
679
- e.originalEvent.uiTreeDragging = true;
680
- }
681
- e.preventDefault();
682
- eventObj = UiTreeHelper.eventObj(e);
683
-
684
- firstMoving = true;
685
- dragInfo = UiTreeHelper.dragInfo(scope);
686
-
687
- tagName = element.prop('tagName');
688
-
689
- if (tagName.toLowerCase() === 'tr') {
690
- placeElm = angular.element($window.document.createElement(tagName));
691
- tdElm = angular.element($window.document.createElement('td'))
692
- .addClass(config.placeholderClass)
693
- .attr('colspan', element[0].children.length);
694
- placeElm.append(tdElm);
695
- } else {
696
- placeElm = angular.element($window.document.createElement(tagName))
697
- .addClass(config.placeholderClass);
698
- }
699
- hiddenPlaceElm = angular.element($window.document.createElement(tagName));
700
- if (config.hiddenClass) {
701
- hiddenPlaceElm.addClass(config.hiddenClass);
702
- }
703
-
704
- pos = UiTreeHelper.positionStarted(eventObj, element);
705
- placeElm.css('height', UiTreeHelper.height(element) + 'px');
706
-
707
- dragElm = angular.element($window.document.createElement(scope.$parentNodesScope.$element.prop('tagName')))
708
- .addClass(scope.$parentNodesScope.$element.attr('class')).addClass(config.dragClass);
709
- dragElm.css('width', UiTreeHelper.width(element) + 'px');
710
- dragElm.css('z-index', 9999);
711
-
712
- // Prevents cursor to change rapidly in Opera 12.16 and IE when dragging an element
713
- hStyle = (element[0].querySelector('.angular-ui-tree-handle') || element[0]).currentStyle;
714
- if (hStyle) {
715
- document.body.setAttribute('ui-tree-cursor', $document.find('body').css('cursor') || '');
716
- $document.find('body').css({'cursor': hStyle.cursor + '!important'});
717
- }
718
-
719
- if (scope.sourceOnly) {
720
- placeElm.css('display', 'none');
721
- }
722
- element.after(placeElm);
723
- element.after(hiddenPlaceElm);
724
- if (dragInfo.isClone() && scope.sourceOnly) {
725
- dragElm.append(cloneElm);
726
- } else {
727
- dragElm.append(element);
728
- }
729
-
730
- $rootElement.append(dragElm);
731
-
732
- dragElm.css({
733
- 'left': eventObj.pageX - pos.offsetX + 'px',
734
- 'top': eventObj.pageY - pos.offsetY + 'px'
735
- });
736
- elements = {
737
- placeholder: placeElm,
738
- dragging: dragElm
739
- };
740
-
741
- bindDragMoveEvents();
742
- // Fire dragStart callback
743
- scope.$apply(function () {
744
- scope.$treeScope.$callbacks.dragStart(dragInfo.eventArgs(elements, pos));
745
- });
746
-
747
- document_height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
748
- document_width = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
749
- };
750
-
751
- dragMove = function (e) {
752
- var eventObj = UiTreeHelper.eventObj(e),
753
- prev,
754
- next,
755
- leftElmPos,
756
- topElmPos,
757
- top_scroll,
758
- bottom_scroll,
759
- target,
760
- decrease,
761
- targetX,
762
- targetY,
763
- displayElm,
764
- targetNode,
765
- targetElm,
766
- isEmpty,
767
- targetOffset,
768
- targetBefore;
769
-
770
- if (dragElm) {
771
- e.preventDefault();
772
-
773
- if ($window.getSelection) {
774
- $window.getSelection().removeAllRanges();
775
- } else if ($window.document.selection) {
776
- $window.document.selection.empty();
777
- }
778
-
779
- leftElmPos = eventObj.pageX - pos.offsetX;
780
- topElmPos = eventObj.pageY - pos.offsetY;
781
-
782
- //dragElm can't leave the screen on the left
783
- if (leftElmPos < 0) {
784
- leftElmPos = 0;
785
- }
786
-
787
- //dragElm can't leave the screen on the top
788
- if (topElmPos < 0) {
789
- topElmPos = 0;
790
- }
791
-
792
- //dragElm can't leave the screen on the bottom
793
- if ((topElmPos + 10) > document_height) {
794
- topElmPos = document_height - 10;
795
- }
796
-
797
- //dragElm can't leave the screen on the right
798
- if ((leftElmPos + 10) > document_width) {
799
- leftElmPos = document_width - 10;
800
- }
801
-
802
- dragElm.css({
803
- 'left': leftElmPos + 'px',
804
- 'top': topElmPos + 'px'
805
- });
806
-
807
- top_scroll = window.pageYOffset || $window.document.documentElement.scrollTop;
808
- bottom_scroll = top_scroll + (window.innerHeight || $window.document.clientHeight || $window.document.clientHeight);
809
-
810
- // to scroll down if cursor y-position is greater than the bottom position the vertical scroll
811
- if (bottom_scroll < eventObj.pageY && bottom_scroll <= document_height) {
812
- window.scrollBy(0, 10);
813
- }
814
-
815
- // to scroll top if cursor y-position is less than the top position the vertical scroll
816
- if (top_scroll > eventObj.pageY) {
817
- window.scrollBy(0, -10);
818
- }
819
-
820
- UiTreeHelper.positionMoved(e, pos, firstMoving);
821
- if (firstMoving) {
822
- firstMoving = false;
823
- return;
824
- }
825
-
826
- // check if add it as a child node first
827
- // todo decrease is unused
828
- decrease = (UiTreeHelper.offset(dragElm).left - UiTreeHelper.offset(placeElm).left) >= config.threshold;
829
-
830
- targetX = eventObj.pageX - ($window.pageXOffset ||
831
- $window.document.body.scrollLeft ||
832
- $window.document.documentElement.scrollLeft) -
833
- ($window.document.documentElement.clientLeft || 0);
834
-
835
- targetY = eventObj.pageY - ($window.pageYOffset ||
836
- $window.document.body.scrollTop ||
837
- $window.document.documentElement.scrollTop) -
838
- ($window.document.documentElement.clientTop || 0);
839
-
840
- // Select the drag target. Because IE does not support CSS 'pointer-events: none', it will always
841
- // pick the drag element itself as the target. To prevent this, we hide the drag element while
842
- // selecting the target.
843
- if (angular.isFunction(dragElm.hide)) {
844
- dragElm.hide();
845
- } else {
846
- displayElm = dragElm[0].style.display;
847
- dragElm[0].style.display = 'none';
848
- }
849
-
850
- // when using elementFromPoint() inside an iframe, you have to call
851
- // elementFromPoint() twice to make sure IE8 returns the correct value
852
- $window.document.elementFromPoint(targetX, targetY);
853
-
854
- targetElm = angular.element($window.document.elementFromPoint(targetX, targetY));
855
-
856
- // if the target element is a child element of a ui-tree-handle,
857
- // use the containing handle element as target element
858
- isHandleChild = UiTreeHelper.treeNodeHandlerContainerOfElement(targetElm);
859
- if (isHandleChild) {
860
- targetElm = angular.element(isHandleChild);
861
- }
862
-
863
- if (angular.isFunction(dragElm.show)) {
864
- dragElm.show();
865
- } else {
866
- dragElm[0].style.display = displayElm;
867
- }
868
-
869
- outOfBounds = !UiTreeHelper.elementIsTreeNodeHandle(targetElm) &&
870
- !UiTreeHelper.elementIsTreeNode(targetElm) &&
871
- !UiTreeHelper.elementIsTreeNodes(targetElm) &&
872
- !UiTreeHelper.elementIsTree(targetElm) &&
873
- !UiTreeHelper.elementIsPlaceholder(targetElm);
874
-
875
- // Detect out of bounds condition, update drop target display, and prevent drop
876
- if (outOfBounds) {
877
-
878
- // Remove the placeholder
879
- placeElm.remove();
880
-
881
- // If the target was an empty tree, replace the empty element placeholder
882
- if (treeScope) {
883
- treeScope.resetEmptyElement();
884
- treeScope = null;
885
- }
886
- }
887
-
888
- // move horizontal
889
- if (pos.dirAx && pos.distAxX >= config.levelThreshold) {
890
- pos.distAxX = 0;
891
-
892
- // increase horizontal level if previous sibling exists and is not collapsed
893
- if (pos.distX > 0) {
894
- prev = dragInfo.prev();
895
- if (prev && !prev.collapsed
896
- && prev.accept(scope, prev.childNodesCount())) {
897
- prev.$childNodesScope.$element.append(placeElm);
898
- dragInfo.moveTo(prev.$childNodesScope, prev.childNodes(), prev.childNodesCount());
899
- }
900
- }
901
-
902
- // decrease horizontal level
903
- if (pos.distX < 0) {
904
- // we can't decrease a level if an item preceeds the current one
905
- next = dragInfo.next();
906
- if (!next) {
907
- target = dragInfo.parentNode(); // As a sibling of it's parent node
908
- if (target
909
- && target.$parentNodesScope.accept(scope, target.index() + 1)) {
910
- target.$element.after(placeElm);
911
- dragInfo.moveTo(target.$parentNodesScope, target.siblings(), target.index() + 1);
912
- }
913
- }
914
- }
915
- }
916
-
917
- // move vertical
918
- if (!pos.dirAx) {
919
- if (UiTreeHelper.elementIsTree(targetElm)) {
920
- targetNode = targetElm.controller('uiTree').scope;
921
- } else if (UiTreeHelper.elementIsTreeNodeHandle(targetElm)) {
922
- targetNode = targetElm.controller('uiTreeHandle').scope;
923
- } else if (UiTreeHelper.elementIsTreeNode(targetElm)) {
924
- targetNode = targetElm.controller('uiTreeNode').scope;
925
- } else if (UiTreeHelper.elementIsTreeNodes(targetElm)) {
926
- targetNode = targetElm.controller('uiTreeNodes').scope;
927
- } else if (UiTreeHelper.elementIsPlaceholder(targetElm)) {
928
- targetNode = targetElm.controller('uiTreeNodes').scope;
929
- } else if (targetElm.controller('uiTreeNode')) {
930
- // is a child element of a node
931
- targetNode = targetElm.controller('uiTreeNode').scope;
932
- }
933
-
934
- // check it's new position
935
- isEmpty = false;
936
- if (!targetNode) {
937
- return;
938
- }
939
-
940
- // Show the placeholder if it was hidden for nodrop-enabled and this is a new tree
941
- if (targetNode.$treeScope && !targetNode.$parent.nodropEnabled && !targetNode.$treeScope.nodropEnabled) {
942
- placeElm.css('display', '');
943
- }
944
-
945
- if (targetNode.$type == 'uiTree' && targetNode.dragEnabled) {
946
- isEmpty = targetNode.isEmpty(); // Check if it's empty tree
947
- }
948
-
949
- if (targetNode.$type == 'uiTreeHandle') {
950
- targetNode = targetNode.$nodeScope;
951
- }
952
-
953
- if (targetNode.$type != 'uiTreeNode'
954
- && !isEmpty) { // Check if it is a uiTreeNode or it's an empty tree
955
- return;
956
- }
957
-
958
- // if placeholder move from empty tree, reset it.
959
- if (treeScope && placeElm.parent()[0] != treeScope.$element[0]) {
960
- treeScope.resetEmptyElement();
961
- treeScope = null;
962
- }
963
-
964
- if (isEmpty) { // it's an empty tree
965
- treeScope = targetNode;
966
- if (targetNode.$nodesScope.accept(scope, 0)) {
967
- targetNode.place(placeElm);
968
- dragInfo.moveTo(targetNode.$nodesScope, targetNode.$nodesScope.childNodes(), 0);
969
- }
970
- } else if (targetNode.dragEnabled()) { // drag enabled
971
- targetElm = targetNode.$element; // Get the element of ui-tree-node
972
- targetOffset = UiTreeHelper.offset(targetElm);
973
- targetBefore = targetNode.horizontal ? eventObj.pageX < (targetOffset.left + UiTreeHelper.width(targetElm) / 2)
974
- : eventObj.pageY < (targetOffset.top + UiTreeHelper.height(targetElm) / 2);
975
-
976
- if (targetNode.$parentNodesScope.accept(scope, targetNode.index())) {
977
- if (targetBefore) {
978
- targetElm[0].parentNode.insertBefore(placeElm[0], targetElm[0]);
979
- dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index());
980
- } else {
981
- targetElm.after(placeElm);
982
- dragInfo.moveTo(targetNode.$parentNodesScope, targetNode.siblings(), targetNode.index() + 1);
983
- }
984
- } else if (!targetBefore && targetNode.accept(scope, targetNode.childNodesCount())) { // we have to check if it can add the dragging node as a child
985
- targetNode.$childNodesScope.$element.append(placeElm);
986
- dragInfo.moveTo(targetNode.$childNodesScope, targetNode.childNodes(), targetNode.childNodesCount());
987
- } else {
988
- outOfBounds = true;
989
- }
990
- }
991
- }
992
-
993
- scope.$apply(function () {
994
- scope.$treeScope.$callbacks.dragMove(dragInfo.eventArgs(elements, pos));
995
- });
996
- }
997
- };
998
-
999
- dragEnd = function (e) {
1000
- var dragEventArgs = dragInfo.eventArgs(elements, pos);
1001
- e.preventDefault();
1002
- unbindDragMoveEvents();
1003
-
1004
- scope.$treeScope.$apply(function () {
1005
- $q.when(scope.$treeScope.$callbacks.beforeDrop(dragEventArgs))
1006
- // promise resolved (or callback didn't return false)
1007
- .then(function (allowDrop) {
1008
- if (allowDrop !== false && scope.$$allowNodeDrop && !outOfBounds) { // node drop accepted)
1009
- dragInfo.apply();
1010
- // fire the dropped callback only if the move was successful
1011
- scope.$treeScope.$callbacks.dropped(dragEventArgs);
1012
- } else { // drop canceled - revert the node to its original position
1013
- bindDragStartEvents();
1014
- }
1015
- })
1016
- // promise rejected - revert the node to its original position
1017
- .catch(function () {
1018
- bindDragStartEvents();
1019
- })
1020
- .finally(function () {
1021
- hiddenPlaceElm.replaceWith(scope.$element);
1022
- placeElm.remove();
1023
-
1024
- if (dragElm) { // drag element is attached to the mouse pointer
1025
- dragElm.remove();
1026
- dragElm = null;
1027
- }
1028
- scope.$treeScope.$callbacks.dragStop(dragEventArgs);
1029
- scope.$$allowNodeDrop = false;
1030
- dragInfo = null;
1031
-
1032
- // Restore cursor in Opera 12.16 and IE
1033
- var oldCur = document.body.getAttribute('ui-tree-cursor');
1034
- if (oldCur !== null) {
1035
- $document.find('body').css({'cursor': oldCur});
1036
- document.body.removeAttribute('ui-tree-cursor');
1037
- }
1038
- });
1039
- });
1040
- };
1041
-
1042
- dragStartEvent = function (e) {
1043
- if (scope.dragEnabled()) {
1044
- dragStart(e);
1045
- }
1046
- };
1047
-
1048
- dragMoveEvent = function (e) {
1049
- dragMove(e);
1050
- };
1051
-
1052
- dragEndEvent = function (e) {
1053
- scope.$$allowNodeDrop = true;
1054
- dragEnd(e);
1055
- };
1056
-
1057
- dragCancelEvent = function (e) {
1058
- dragEnd(e);
1059
- };
1060
-
1061
- dragDelay = (function () {
1062
- var to;
1063
-
1064
- return {
1065
- exec: function (fn, ms) {
1066
- if (!ms) {
1067
- ms = 0;
1068
- }
1069
- this.cancel();
1070
- to = $timeout(fn, ms);
1071
- },
1072
- cancel: function () {
1073
- $timeout.cancel(to);
1074
- }
1075
- };
1076
- })();
1077
-
1078
- /**
1079
- * Binds the mouse/touch events to enable drag start for this node
1080
- */
1081
- bindDragStartEvents = function () {
1082
- element.bind('touchstart mousedown', function (e) {
1083
- dragDelay.exec(function () {
1084
- dragStartEvent(e);
1085
- }, scope.dragDelay || 0);
1086
- });
1087
- element.bind('touchend touchcancel mouseup', function () {
1088
- dragDelay.cancel();
1089
- });
1090
- };
1091
- bindDragStartEvents();
1092
-
1093
- /**
1094
- * Binds mouse/touch events that handle moving/dropping this dragged node
1095
- */
1096
- bindDragMoveEvents = function () {
1097
- angular.element($document).bind('touchend', dragEndEvent);
1098
- angular.element($document).bind('touchcancel', dragEndEvent);
1099
- angular.element($document).bind('touchmove', dragMoveEvent);
1100
- angular.element($document).bind('mouseup', dragEndEvent);
1101
- angular.element($document).bind('mousemove', dragMoveEvent);
1102
- angular.element($document).bind('mouseleave', dragCancelEvent);
1103
- };
1104
-
1105
- /**
1106
- * Unbinds mouse/touch events that handle moving/dropping this dragged node
1107
- */
1108
- unbindDragMoveEvents = function () {
1109
- angular.element($document).unbind('touchend', dragEndEvent);
1110
- angular.element($document).unbind('touchcancel', dragEndEvent);
1111
- angular.element($document).unbind('touchmove', dragMoveEvent);
1112
- angular.element($document).unbind('mouseup', dragEndEvent);
1113
- angular.element($document).unbind('mousemove', dragMoveEvent);
1114
- angular.element($document).unbind('mouseleave', dragCancelEvent);
1115
- };
1116
-
1117
- keydownHandler = function (e) {
1118
- if (e.keyCode == 27) {
1119
- scope.$$allowNodeDrop = false;
1120
- dragEnd(e);
1121
- }
1122
- };
1123
-
1124
- angular.element($window.document).bind('keydown', keydownHandler);
1125
-
1126
- //unbind handler that retains scope
1127
- scope.$on('$destroy', function () {
1128
- angular.element($window.document).unbind('keydown', keydownHandler);
1129
- });
1130
- }
1131
- };
1132
- }
1133
- ]);
1134
-
1135
- })();
1136
-
1137
- (function () {
1138
- 'use strict';
1139
-
1140
- angular.module('ui.tree')
1141
- .directive('uiTreeNodes', ['treeConfig', '$window',
1142
- function (treeConfig) {
1143
- return {
1144
- require: ['ngModel', '?^uiTreeNode', '^uiTree'],
1145
- restrict: 'A',
1146
- scope: true,
1147
- controller: 'TreeNodesController',
1148
- link: function (scope, element, attrs, controllersArr) {
1149
-
1150
- var config = {},
1151
- ngModel = controllersArr[0],
1152
- treeNodeCtrl = controllersArr[1],
1153
- treeCtrl = controllersArr[2];
1154
-
1155
- angular.extend(config, treeConfig);
1156
- if (config.nodesClass) {
1157
- element.addClass(config.nodesClass);
1158
- }
1159
-
1160
- if (treeNodeCtrl) {
1161
- treeNodeCtrl.scope.$childNodesScope = scope;
1162
- scope.$nodeScope = treeNodeCtrl.scope;
1163
- } else {
1164
- // find the root nodes if there is no parent node and have a parent ui-tree
1165
- treeCtrl.scope.$nodesScope = scope;
1166
- }
1167
- scope.$treeScope = treeCtrl.scope;
1168
-
1169
- if (ngModel) {
1170
- ngModel.$render = function () {
1171
- scope.$modelValue = ngModel.$modelValue;
1172
- };
1173
- }
1174
-
1175
- scope.$watch(function () {
1176
- return attrs.maxDepth;
1177
- }, function (val) {
1178
- if ((typeof val) == 'number') {
1179
- scope.maxDepth = val;
1180
- }
1181
- });
1182
-
1183
- scope.$watch(function () {
1184
- return attrs.nodropEnabled;
1185
- }, function (newVal) {
1186
- if ((typeof newVal) != 'undefined') {
1187
- scope.nodropEnabled = true;
1188
- }
1189
- }, true);
1190
-
1191
- attrs.$observe('horizontal', function (val) {
1192
- scope.horizontal = ((typeof val) != 'undefined');
1193
- });
1194
-
1195
- }
1196
- };
1197
- }
1198
- ]);
1199
- })();
1200
-
1201
- (function () {
1202
- 'use strict';
1203
-
1204
- angular.module('ui.tree')
1205
-
1206
- /**
1207
- * @ngdoc service
1208
- * @name ui.tree.service:UiTreeHelper
1209
- * @requires ng.$document
1210
- * @requires ng.$window
1211
- *
1212
- * @description
1213
- * angular-ui-tree.
1214
- */
1215
- .factory('UiTreeHelper', ['$document', '$window', 'treeConfig',
1216
- function ($document, $window, treeConfig) {
1217
- return {
1218
-
1219
- /**
1220
- * A hashtable used to storage data of nodes
1221
- * @type {Object}
1222
- */
1223
- nodesData: {},
1224
-
1225
- setNodeAttribute: function (scope, attrName, val) {
1226
- if (!scope.$modelValue) {
1227
- return null;
1228
- }
1229
- var data = this.nodesData[scope.$modelValue.$$hashKey];
1230
- if (!data) {
1231
- data = {};
1232
- this.nodesData[scope.$modelValue.$$hashKey] = data;
1233
- }
1234
- data[attrName] = val;
1235
- },
1236
-
1237
- getNodeAttribute: function (scope, attrName) {
1238
- if (!scope.$modelValue) {
1239
- return null;
1240
- }
1241
- var data = this.nodesData[scope.$modelValue.$$hashKey];
1242
- if (data) {
1243
- return data[attrName];
1244
- }
1245
- return null;
1246
- },
1247
-
1248
- /**
1249
- * @ngdoc method
1250
- * @methodOf ui.tree.service:$nodrag
1251
- * @param {Object} targetElm angular element
1252
- * @return {Bool} check if the node can be dragged.
1253
- */
1254
- nodrag: function (targetElm) {
1255
- if (typeof targetElm.attr('data-nodrag') != 'undefined') {
1256
- return targetElm.attr('data-nodrag') !== 'false';
1257
- }
1258
- return false;
1259
- },
1260
-
1261
- /**
1262
- * get the event object for touches
1263
- * @param {[type]} e [description]
1264
- * @return {[type]} [description]
1265
- */
1266
- eventObj: function (e) {
1267
- var obj = e;
1268
- if (e.targetTouches !== undefined) {
1269
- obj = e.targetTouches.item(0);
1270
- } else if (e.originalEvent !== undefined && e.originalEvent.targetTouches !== undefined) {
1271
- obj = e.originalEvent.targetTouches.item(0);
1272
- }
1273
- return obj;
1274
- },
1275
-
1276
- dragInfo: function (node) {
1277
- return {
1278
- source: node,
1279
- sourceInfo: {
1280
- cloneModel: node.$treeScope.cloneEnabled === true ? angular.copy(node.$modelValue) : undefined,
1281
- nodeScope: node,
1282
- index: node.index(),
1283
- nodesScope: node.$parentNodesScope
1284
- },
1285
- index: node.index(),
1286
- siblings: node.siblings().slice(0),
1287
- parent: node.$parentNodesScope,
1288
-
1289
- // Move the node to a new position
1290
- moveTo: function (parent, siblings, index) {
1291
- this.parent = parent;
1292
- this.siblings = siblings.slice(0);
1293
-
1294
- // If source node is in the target nodes
1295
- var i = this.siblings.indexOf(this.source);
1296
- if (i > -1) {
1297
- this.siblings.splice(i, 1);
1298
- if (this.source.index() < index) {
1299
- index--;
1300
- }
1301
- }
1302
-
1303
- this.siblings.splice(index, 0, this.source);
1304
- this.index = index;
1305
- },
1306
-
1307
- parentNode: function () {
1308
- return this.parent.$nodeScope;
1309
- },
1310
-
1311
- prev: function () {
1312
- if (this.index > 0) {
1313
- return this.siblings[this.index - 1];
1314
- }
1315
-
1316
- return null;
1317
- },
1318
-
1319
- next: function () {
1320
- if (this.index < this.siblings.length - 1) {
1321
- return this.siblings[this.index + 1];
1322
- }
1323
-
1324
- return null;
1325
- },
1326
-
1327
- isClone: function () {
1328
- return this.source.$treeScope.cloneEnabled === true;
1329
- },
1330
-
1331
- clonedNode: function (node) {
1332
- return angular.copy(node);
1333
- },
1334
-
1335
- isDirty: function () {
1336
- return this.source.$parentNodesScope != this.parent ||
1337
- this.source.index() != this.index;
1338
- },
1339
-
1340
- isForeign: function () {
1341
- return this.source.$treeScope !== this.parent.$treeScope;
1342
- },
1343
-
1344
- eventArgs: function (elements, pos) {
1345
- return {
1346
- source: this.sourceInfo,
1347
- dest: {
1348
- index: this.index,
1349
- nodesScope: this.parent
1350
- },
1351
- elements: elements,
1352
- pos: pos
1353
- };
1354
- },
1355
-
1356
- apply: function () {
1357
-
1358
- var nodeData = this.source.$modelValue;
1359
-
1360
- // nodrop enabled on tree or parent
1361
- if (this.parent.nodropEnabled || this.parent.$treeScope.nodropEnabled) {
1362
- return;
1363
- }
1364
-
1365
- // node was dropped in the same place - do nothing
1366
- if (!this.isDirty()) {
1367
- return;
1368
- }
1369
-
1370
- // cloneEnabled and cross-tree so copy and do not remove from source
1371
- if (this.isClone() && this.isForeign()) {
1372
- this.parent.insertNode(this.index, this.sourceInfo.cloneModel);
1373
- } else { // Any other case, remove and reinsert
1374
- this.source.remove();
1375
- this.parent.insertNode(this.index, nodeData);
1376
- }
1377
- }
1378
- };
1379
- },
1380
-
1381
- /**
1382
- * @ngdoc method
1383
- * @name ui.tree#height
1384
- * @methodOf ui.tree.service:UiTreeHelper
1385
- *
1386
- * @description
1387
- * Get the height of an element.
1388
- *
1389
- * @param {Object} element Angular element.
1390
- * @returns {String} Height
1391
- */
1392
- height: function (element) {
1393
- return element.prop('scrollHeight');
1394
- },
1395
-
1396
- /**
1397
- * @ngdoc method
1398
- * @name ui.tree#width
1399
- * @methodOf ui.tree.service:UiTreeHelper
1400
- *
1401
- * @description
1402
- * Get the width of an element.
1403
- *
1404
- * @param {Object} element Angular element.
1405
- * @returns {String} Width
1406
- */
1407
- width: function (element) {
1408
- return element.prop('scrollWidth');
1409
- },
1410
-
1411
- /**
1412
- * @ngdoc method
1413
- * @name ui.tree#offset
1414
- * @methodOf ui.nestedSortable.service:UiTreeHelper
1415
- *
1416
- * @description
1417
- * Get the offset values of an element.
1418
- *
1419
- * @param {Object} element Angular element.
1420
- * @returns {Object} Object with properties width, height, top and left
1421
- */
1422
- offset: function (element) {
1423
- var boundingClientRect = element[0].getBoundingClientRect();
1424
-
1425
- return {
1426
- width: element.prop('offsetWidth'),
1427
- height: element.prop('offsetHeight'),
1428
- top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
1429
- left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
1430
- };
1431
- },
1432
-
1433
- /**
1434
- * @ngdoc method
1435
- * @name ui.tree#positionStarted
1436
- * @methodOf ui.tree.service:UiTreeHelper
1437
- *
1438
- * @description
1439
- * Get the start position of the target element according to the provided event properties.
1440
- *
1441
- * @param {Object} e Event
1442
- * @param {Object} target Target element
1443
- * @returns {Object} Object with properties offsetX, offsetY, startX, startY, nowX and dirX.
1444
- */
1445
- positionStarted: function (e, target) {
1446
- var pos = {},
1447
- pageX = e.pageX,
1448
- pageY = e.pageY;
1449
-
1450
- if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
1451
- pageX = e.originalEvent.touches[0].pageX;
1452
- pageY = e.originalEvent.touches[0].pageY;
1453
- }
1454
- pos.offsetX = pageX - this.offset(target).left;
1455
- pos.offsetY = pageY - this.offset(target).top;
1456
- pos.startX = pos.lastX = pageX;
1457
- pos.startY = pos.lastY = pageY;
1458
- pos.nowX = pos.nowY = pos.distX = pos.distY = pos.dirAx = 0;
1459
- pos.dirX = pos.dirY = pos.lastDirX = pos.lastDirY = pos.distAxX = pos.distAxY = 0;
1460
- return pos;
1461
- },
1462
-
1463
- positionMoved: function (e, pos, firstMoving) {
1464
- var pageX = e.pageX,
1465
- pageY = e.pageY,
1466
- newAx;
1467
- if (e.originalEvent && e.originalEvent.touches && (e.originalEvent.touches.length > 0)) {
1468
- pageX = e.originalEvent.touches[0].pageX;
1469
- pageY = e.originalEvent.touches[0].pageY;
1470
- }
1471
- // mouse position last events
1472
- pos.lastX = pos.nowX;
1473
- pos.lastY = pos.nowY;
1474
-
1475
- // mouse position this events
1476
- pos.nowX = pageX;
1477
- pos.nowY = pageY;
1478
-
1479
- // distance mouse moved between events
1480
- pos.distX = pos.nowX - pos.lastX;
1481
- pos.distY = pos.nowY - pos.lastY;
1482
-
1483
- // direction mouse was moving
1484
- pos.lastDirX = pos.dirX;
1485
- pos.lastDirY = pos.dirY;
1486
-
1487
- // direction mouse is now moving (on both axis)
1488
- pos.dirX = pos.distX === 0 ? 0 : pos.distX > 0 ? 1 : -1;
1489
- pos.dirY = pos.distY === 0 ? 0 : pos.distY > 0 ? 1 : -1;
1490
-
1491
- // axis mouse is now moving on
1492
- newAx = Math.abs(pos.distX) > Math.abs(pos.distY) ? 1 : 0;
1493
-
1494
- // do nothing on first move
1495
- if (firstMoving) {
1496
- pos.dirAx = newAx;
1497
- pos.moving = true;
1498
- return;
1499
- }
1500
-
1501
- // calc distance moved on this axis (and direction)
1502
- if (pos.dirAx !== newAx) {
1503
- pos.distAxX = 0;
1504
- pos.distAxY = 0;
1505
- } else {
1506
- pos.distAxX += Math.abs(pos.distX);
1507
- if (pos.dirX !== 0 && pos.dirX !== pos.lastDirX) {
1508
- pos.distAxX = 0;
1509
- }
1510
-
1511
- pos.distAxY += Math.abs(pos.distY);
1512
- if (pos.dirY !== 0 && pos.dirY !== pos.lastDirY) {
1513
- pos.distAxY = 0;
1514
- }
1515
- }
1516
-
1517
- pos.dirAx = newAx;
1518
- },
1519
-
1520
- elementIsTreeNode: function (element) {
1521
- return typeof element.attr('ui-tree-node') !== 'undefined';
1522
- },
1523
-
1524
- elementIsTreeNodeHandle: function (element) {
1525
- return typeof element.attr('ui-tree-handle') !== 'undefined';
1526
- },
1527
- elementIsTree: function (element) {
1528
- return typeof element.attr('ui-tree') !== 'undefined';
1529
- },
1530
- elementIsTreeNodes: function (element) {
1531
- return typeof element.attr('ui-tree-nodes') !== 'undefined';
1532
- },
1533
- elementIsPlaceholder: function (element) {
1534
- return element.hasClass(treeConfig.placeholderClass);
1535
- },
1536
- elementContainsTreeNodeHandler: function (element) {
1537
- return element[0].querySelectorAll('[ui-tree-handle]').length >= 1;
1538
- },
1539
- treeNodeHandlerContainerOfElement: function (element) {
1540
- return findFirstParentElementWithAttribute('ui-tree-handle', element[0]);
1541
- }
1542
- };
1543
- }
1544
- ]);
1545
-
1546
- // TODO: optimize this loop
1547
- function findFirstParentElementWithAttribute(attributeName, childObj) {
1548
- // undefined if the mouse leaves the browser window
1549
- if (childObj === undefined) {
1550
- return null;
1551
- }
1552
- var testObj = childObj.parentNode,
1553
- count = 1,
1554
- // check for setAttribute due to exception thrown by Firefox when a node is dragged outside the browser window
1555
- res = (typeof testObj.setAttribute === 'function' && testObj.hasAttribute(attributeName)) ? testObj : null;
1556
- while (testObj && typeof testObj.setAttribute === 'function' && !testObj.hasAttribute(attributeName)) {
1557
- testObj = testObj.parentNode;
1558
- res = testObj;
1559
- if (testObj === document.documentElement) {
1560
- res = null;
1561
- break;
1562
- }
1563
- count++;
1564
- }
1565
-
1566
- return res;
1567
- }
1568
-
1569
- })();