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.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Rakefile +138 -0
- data/conf/message.yaml +93 -0
- data/conf/message_pl.yaml +93 -0
- data/engine2.gemspec +34 -0
- data/lib/engine2.rb +34 -0
- data/lib/engine2/action.rb +217 -0
- data/lib/engine2/core.rb +572 -0
- data/lib/engine2/handler.rb +134 -0
- data/lib/engine2/meta.rb +969 -0
- data/lib/engine2/meta/decode_meta.rb +110 -0
- data/lib/engine2/meta/delete_meta.rb +73 -0
- data/lib/engine2/meta/form_meta.rb +144 -0
- data/lib/engine2/meta/infra_meta.rb +292 -0
- data/lib/engine2/meta/link_meta.rb +133 -0
- data/lib/engine2/meta/list_meta.rb +284 -0
- data/lib/engine2/meta/save_meta.rb +63 -0
- data/lib/engine2/meta/view_meta.rb +22 -0
- data/lib/engine2/model.rb +390 -0
- data/lib/engine2/models/Files.rb +38 -0
- data/lib/engine2/models/UserInfo.rb +24 -0
- data/lib/engine2/post_bootstrap.rb +83 -0
- data/lib/engine2/pre_bootstrap.rb +27 -0
- data/lib/engine2/scheme.rb +202 -0
- data/lib/engine2/templates.rb +229 -0
- data/lib/engine2/type_info.rb +342 -0
- data/lib/engine2/version.rb +9 -0
- data/public/assets/javascripts.js +13 -0
- data/public/assets/styles.css +4 -0
- data/public/css/angular-motion.css +1022 -0
- data/public/css/angular-ui-tree.min.css +1 -0
- data/public/css/app.css +196 -0
- data/public/css/bootstrap-additions.css +1560 -0
- data/public/css/bootstrap.min.css +11 -0
- data/public/css/font-awesome.min.css +4 -0
- data/public/favicon.ico +0 -0
- data/public/fonts/FontAwesome.otf +0 -0
- data/public/fonts/fontawesome-webfont.eot +0 -0
- data/public/fonts/fontawesome-webfont.svg +655 -0
- data/public/fonts/fontawesome-webfont.ttf +0 -0
- data/public/fonts/fontawesome-webfont.woff +0 -0
- data/public/fonts/fontawesome-webfont.woff2 +0 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +288 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/public/images/file.png +0 -0
- data/public/images/folder-closed.png +0 -0
- data/public/images/folder.png +0 -0
- data/public/images/node-closed-2.png +0 -0
- data/public/images/node-closed-light.png +0 -0
- data/public/images/node-closed.png +0 -0
- data/public/images/node-opened-2.png +0 -0
- data/public/images/node-opened-light.png +0 -0
- data/public/images/node-opened.png +0 -0
- data/public/img/ajax-loader-dark.gif +0 -0
- data/public/img/ajax-loader-light.gif +0 -0
- data/public/img/ajax-loader.gif +0 -0
- data/public/js/angular-animate.js +4115 -0
- data/public/js/angular-cookies.js +322 -0
- data/public/js/angular-local-storage.js +455 -0
- data/public/js/angular-route.js +1022 -0
- data/public/js/angular-sanitize.js +717 -0
- data/public/js/angular-strap.js +4339 -0
- data/public/js/angular-strap.tpl.js +43 -0
- data/public/js/angular-ui-tree.js +1569 -0
- data/public/js/angular.js +30714 -0
- data/public/js/i18n/angular-locale_pl.js +115 -0
- data/public/js/lodash.custom.min.js +97 -0
- data/public/js/ng-file-upload-shim.min.js +2 -0
- data/public/js/ng-file-upload.min.js +3 -0
- data/views/app.coffee +3 -0
- data/views/engine2.coffee +557 -0
- data/views/engine2actions.coffee +849 -0
- data/views/engine2templates.coffee +0 -0
- data/views/fields/blob.slim +22 -0
- data/views/fields/bs_select.slim +10 -0
- data/views/fields/bsselect_picker.slim +18 -0
- data/views/fields/bsselect_picker_opt.slim +22 -0
- data/views/fields/checkbox.slim +11 -0
- data/views/fields/checkbox_buttons.slim +6 -0
- data/views/fields/checkbox_buttons_opt.slim +8 -0
- data/views/fields/currency.slim +10 -0
- data/views/fields/date.slim +21 -0
- data/views/fields/date_range.slim +44 -0
- data/views/fields/date_time.slim +42 -0
- data/views/fields/datetime.slim +42 -0
- data/views/fields/decimal.slim +11 -0
- data/views/fields/decimal_date.slim +22 -0
- data/views/fields/decimal_time.slim +26 -0
- data/views/fields/email.slim +13 -0
- data/views/fields/file_store.slim +61 -0
- data/views/fields/input_text.slim +14 -0
- data/views/fields/integer.slim +11 -0
- data/views/fields/list_bsselect.slim +18 -0
- data/views/fields/list_bsselect_opt.slim +21 -0
- data/views/fields/list_buttons.slim +3 -0
- data/views/fields/list_buttons_opt.slim +5 -0
- data/views/fields/list_select.slim +11 -0
- data/views/fields/list_select_opt.slim +15 -0
- data/views/fields/password.slim +14 -0
- data/views/fields/radio_checkbox.slim +10 -0
- data/views/fields/scaffold.slim +2 -0
- data/views/fields/scaffold_picker.slim +20 -0
- data/views/fields/select_picker.slim +12 -0
- data/views/fields/select_picker_opt.slim +16 -0
- data/views/fields/text_area.slim +10 -0
- data/views/fields/time.slim +22 -0
- data/views/fields/typeahead_picker.slim +25 -0
- data/views/index.slim +44 -0
- data/views/infra/index.slim +5 -0
- data/views/infra/inspect.slim +81 -0
- data/views/modals/close_m.slim +15 -0
- data/views/modals/confirm_m.slim +19 -0
- data/views/modals/empty_m.slim +12 -0
- data/views/modals/menu_m.slim +13 -0
- data/views/modals/yes_no_m.slim +19 -0
- data/views/panels/menu_m.slim +9 -0
- data/views/scaffold/confirm.slim +3 -0
- data/views/scaffold/fields.slim +10 -0
- data/views/scaffold/form.slim +11 -0
- data/views/scaffold/list.slim +42 -0
- data/views/scaffold/message.slim +3 -0
- data/views/scaffold/search.slim +20 -0
- data/views/scaffold/view.slim +18 -0
- data/views/search_fields/bsmselect_picker.slim +25 -0
- data/views/search_fields/bsselect_picker.slim +24 -0
- data/views/search_fields/checkbox.slim +11 -0
- data/views/search_fields/checkbox2.slim +14 -0
- data/views/search_fields/checkbox_buttons.slim +10 -0
- data/views/search_fields/date_range.slim +46 -0
- data/views/search_fields/decimal_date_range.slim +47 -0
- data/views/search_fields/input_text.slim +18 -0
- data/views/search_fields/integer.slim +18 -0
- data/views/search_fields/integer_range.slim +27 -0
- data/views/search_fields/list_bsmselect.slim +24 -0
- data/views/search_fields/list_bsselect.slim +22 -0
- data/views/search_fields/list_buttons.slim +8 -0
- data/views/search_fields/list_select.slim +17 -0
- data/views/search_fields/scaffold_picker.slim +19 -0
- data/views/search_fields/select_picker.slim +17 -0
- data/views/search_fields/typeahead_picker.slim +25 -0
- 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()">×</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()">×</button> <strong ng-bind="title"></strong> <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">×</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> </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"> </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"> </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> </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"> </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
|
+
})();
|