engine2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ })();