engine2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/Rakefile +138 -0
  4. data/conf/message.yaml +93 -0
  5. data/conf/message_pl.yaml +93 -0
  6. data/engine2.gemspec +34 -0
  7. data/lib/engine2.rb +34 -0
  8. data/lib/engine2/action.rb +217 -0
  9. data/lib/engine2/core.rb +572 -0
  10. data/lib/engine2/handler.rb +134 -0
  11. data/lib/engine2/meta.rb +969 -0
  12. data/lib/engine2/meta/decode_meta.rb +110 -0
  13. data/lib/engine2/meta/delete_meta.rb +73 -0
  14. data/lib/engine2/meta/form_meta.rb +144 -0
  15. data/lib/engine2/meta/infra_meta.rb +292 -0
  16. data/lib/engine2/meta/link_meta.rb +133 -0
  17. data/lib/engine2/meta/list_meta.rb +284 -0
  18. data/lib/engine2/meta/save_meta.rb +63 -0
  19. data/lib/engine2/meta/view_meta.rb +22 -0
  20. data/lib/engine2/model.rb +390 -0
  21. data/lib/engine2/models/Files.rb +38 -0
  22. data/lib/engine2/models/UserInfo.rb +24 -0
  23. data/lib/engine2/post_bootstrap.rb +83 -0
  24. data/lib/engine2/pre_bootstrap.rb +27 -0
  25. data/lib/engine2/scheme.rb +202 -0
  26. data/lib/engine2/templates.rb +229 -0
  27. data/lib/engine2/type_info.rb +342 -0
  28. data/lib/engine2/version.rb +9 -0
  29. data/public/assets/javascripts.js +13 -0
  30. data/public/assets/styles.css +4 -0
  31. data/public/css/angular-motion.css +1022 -0
  32. data/public/css/angular-ui-tree.min.css +1 -0
  33. data/public/css/app.css +196 -0
  34. data/public/css/bootstrap-additions.css +1560 -0
  35. data/public/css/bootstrap.min.css +11 -0
  36. data/public/css/font-awesome.min.css +4 -0
  37. data/public/favicon.ico +0 -0
  38. data/public/fonts/FontAwesome.otf +0 -0
  39. data/public/fonts/fontawesome-webfont.eot +0 -0
  40. data/public/fonts/fontawesome-webfont.svg +655 -0
  41. data/public/fonts/fontawesome-webfont.ttf +0 -0
  42. data/public/fonts/fontawesome-webfont.woff +0 -0
  43. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  44. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/public/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/public/images/file.png +0 -0
  50. data/public/images/folder-closed.png +0 -0
  51. data/public/images/folder.png +0 -0
  52. data/public/images/node-closed-2.png +0 -0
  53. data/public/images/node-closed-light.png +0 -0
  54. data/public/images/node-closed.png +0 -0
  55. data/public/images/node-opened-2.png +0 -0
  56. data/public/images/node-opened-light.png +0 -0
  57. data/public/images/node-opened.png +0 -0
  58. data/public/img/ajax-loader-dark.gif +0 -0
  59. data/public/img/ajax-loader-light.gif +0 -0
  60. data/public/img/ajax-loader.gif +0 -0
  61. data/public/js/angular-animate.js +4115 -0
  62. data/public/js/angular-cookies.js +322 -0
  63. data/public/js/angular-local-storage.js +455 -0
  64. data/public/js/angular-route.js +1022 -0
  65. data/public/js/angular-sanitize.js +717 -0
  66. data/public/js/angular-strap.js +4339 -0
  67. data/public/js/angular-strap.tpl.js +43 -0
  68. data/public/js/angular-ui-tree.js +1569 -0
  69. data/public/js/angular.js +30714 -0
  70. data/public/js/i18n/angular-locale_pl.js +115 -0
  71. data/public/js/lodash.custom.min.js +97 -0
  72. data/public/js/ng-file-upload-shim.min.js +2 -0
  73. data/public/js/ng-file-upload.min.js +3 -0
  74. data/views/app.coffee +3 -0
  75. data/views/engine2.coffee +557 -0
  76. data/views/engine2actions.coffee +849 -0
  77. data/views/engine2templates.coffee +0 -0
  78. data/views/fields/blob.slim +22 -0
  79. data/views/fields/bs_select.slim +10 -0
  80. data/views/fields/bsselect_picker.slim +18 -0
  81. data/views/fields/bsselect_picker_opt.slim +22 -0
  82. data/views/fields/checkbox.slim +11 -0
  83. data/views/fields/checkbox_buttons.slim +6 -0
  84. data/views/fields/checkbox_buttons_opt.slim +8 -0
  85. data/views/fields/currency.slim +10 -0
  86. data/views/fields/date.slim +21 -0
  87. data/views/fields/date_range.slim +44 -0
  88. data/views/fields/date_time.slim +42 -0
  89. data/views/fields/datetime.slim +42 -0
  90. data/views/fields/decimal.slim +11 -0
  91. data/views/fields/decimal_date.slim +22 -0
  92. data/views/fields/decimal_time.slim +26 -0
  93. data/views/fields/email.slim +13 -0
  94. data/views/fields/file_store.slim +61 -0
  95. data/views/fields/input_text.slim +14 -0
  96. data/views/fields/integer.slim +11 -0
  97. data/views/fields/list_bsselect.slim +18 -0
  98. data/views/fields/list_bsselect_opt.slim +21 -0
  99. data/views/fields/list_buttons.slim +3 -0
  100. data/views/fields/list_buttons_opt.slim +5 -0
  101. data/views/fields/list_select.slim +11 -0
  102. data/views/fields/list_select_opt.slim +15 -0
  103. data/views/fields/password.slim +14 -0
  104. data/views/fields/radio_checkbox.slim +10 -0
  105. data/views/fields/scaffold.slim +2 -0
  106. data/views/fields/scaffold_picker.slim +20 -0
  107. data/views/fields/select_picker.slim +12 -0
  108. data/views/fields/select_picker_opt.slim +16 -0
  109. data/views/fields/text_area.slim +10 -0
  110. data/views/fields/time.slim +22 -0
  111. data/views/fields/typeahead_picker.slim +25 -0
  112. data/views/index.slim +44 -0
  113. data/views/infra/index.slim +5 -0
  114. data/views/infra/inspect.slim +81 -0
  115. data/views/modals/close_m.slim +15 -0
  116. data/views/modals/confirm_m.slim +19 -0
  117. data/views/modals/empty_m.slim +12 -0
  118. data/views/modals/menu_m.slim +13 -0
  119. data/views/modals/yes_no_m.slim +19 -0
  120. data/views/panels/menu_m.slim +9 -0
  121. data/views/scaffold/confirm.slim +3 -0
  122. data/views/scaffold/fields.slim +10 -0
  123. data/views/scaffold/form.slim +11 -0
  124. data/views/scaffold/list.slim +42 -0
  125. data/views/scaffold/message.slim +3 -0
  126. data/views/scaffold/search.slim +20 -0
  127. data/views/scaffold/view.slim +18 -0
  128. data/views/search_fields/bsmselect_picker.slim +25 -0
  129. data/views/search_fields/bsselect_picker.slim +24 -0
  130. data/views/search_fields/checkbox.slim +11 -0
  131. data/views/search_fields/checkbox2.slim +14 -0
  132. data/views/search_fields/checkbox_buttons.slim +10 -0
  133. data/views/search_fields/date_range.slim +46 -0
  134. data/views/search_fields/decimal_date_range.slim +47 -0
  135. data/views/search_fields/input_text.slim +18 -0
  136. data/views/search_fields/integer.slim +18 -0
  137. data/views/search_fields/integer_range.slim +27 -0
  138. data/views/search_fields/list_bsmselect.slim +24 -0
  139. data/views/search_fields/list_bsselect.slim +22 -0
  140. data/views/search_fields/list_buttons.slim +8 -0
  141. data/views/search_fields/list_select.slim +17 -0
  142. data/views/search_fields/scaffold_picker.slim +19 -0
  143. data/views/search_fields/select_picker.slim +17 -0
  144. data/views/search_fields/typeahead_picker.slim +25 -0
  145. metadata +327 -0
@@ -0,0 +1,43 @@
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);
@@ -0,0 +1,1569 @@
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
+ })();