the_sortable_tree_mongoid 1.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +302 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/images/iconza/blue/add.png +0 -0
  5. data/app/assets/images/iconza/blue/delete.png +0 -0
  6. data/app/assets/images/iconza/blue/down.png +0 -0
  7. data/app/assets/images/iconza/blue/downloads_folder.png +0 -0
  8. data/app/assets/images/iconza/blue/edit.png +0 -0
  9. data/app/assets/images/iconza/blue/move.png +0 -0
  10. data/app/assets/images/iconza/blue/up.png +0 -0
  11. data/app/assets/images/iconza/gray/add.png +0 -0
  12. data/app/assets/images/iconza/gray/delete.png +0 -0
  13. data/app/assets/images/iconza/gray/down.png +0 -0
  14. data/app/assets/images/iconza/gray/edit.png +0 -0
  15. data/app/assets/images/iconza/gray/lock.png +0 -0
  16. data/app/assets/images/iconza/gray/mail.png +0 -0
  17. data/app/assets/images/iconza/gray/push_pin.png +0 -0
  18. data/app/assets/images/iconza/gray/up.png +0 -0
  19. data/app/assets/images/iconza/red/add.png +0 -0
  20. data/app/assets/images/iconza/red/delete.png +0 -0
  21. data/app/assets/images/iconza/red/down.png +0 -0
  22. data/app/assets/images/iconza/red/edit.png +0 -0
  23. data/app/assets/images/iconza/red/newspaper.png +0 -0
  24. data/app/assets/images/iconza/red/trash.png +0 -0
  25. data/app/assets/images/iconza/red/up.png +0 -0
  26. data/app/assets/images/iconza/red/zoom.png +0 -0
  27. data/app/assets/javascripts/jquery.ui.nestedSortable.js +356 -0
  28. data/app/assets/stylesheets/the_sortable_tree.css +120 -0
  29. data/app/assets/stylesheets/the_sortable_tree_min.css +26 -0
  30. data/app/controllers/the_sortable_tree_controller.rb +67 -0
  31. data/app/helpers/the_sortable_tree_helper.rb +69 -0
  32. data/app/views/the_sortable_tree/_children.html.haml +1 -0
  33. data/app/views/the_sortable_tree/_controls.html.haml +16 -0
  34. data/app/views/the_sortable_tree/_js_init_sortable_tree.html.haml +17 -0
  35. data/app/views/the_sortable_tree/_js_on_update_tree.html.haml +12 -0
  36. data/app/views/the_sortable_tree/_js_rebuild_ajax.html.haml +13 -0
  37. data/app/views/the_sortable_tree/_link.html.haml +7 -0
  38. data/app/views/the_sortable_tree/_new.html.haml +3 -0
  39. data/app/views/the_sortable_tree/_node.html.haml +3 -0
  40. data/app/views/the_sortable_tree/_tree.html.haml +6 -0
  41. data/app/views/the_sortable_tree_min/_children.html.haml +1 -0
  42. data/app/views/the_sortable_tree_min/_link.html.haml +1 -0
  43. data/app/views/the_sortable_tree_min/_node.html.haml +3 -0
  44. data/app/views/the_sortable_tree_min/_tree.html.haml +2 -0
  45. data/config/locales/en.yml +10 -0
  46. data/config/locales/ru.yml +10 -0
  47. data/lib/generators/the_sortable_tree/views_generator.rb +29 -0
  48. data/lib/tasks/the_sortable_tree.rake +4 -0
  49. data/lib/the_sortable_tree.rb +18 -0
  50. data/lib/the_sortable_tree/engine.rb +6 -0
  51. data/lib/the_sortable_tree/version.rb +3 -0
  52. data/spec/controlllers/controller_mixin_spec.rb +49 -0
  53. data/spec/dummy/README.rdoc +261 -0
  54. data/spec/dummy/Rakefile +7 -0
  55. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  56. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  57. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  58. data/spec/dummy/app/controllers/tests_controller.rb +3 -0
  59. data/spec/dummy/app/views/layouts/application.html.erb +17 -0
  60. data/spec/dummy/config.ru +4 -0
  61. data/spec/dummy/config/application.rb +56 -0
  62. data/spec/dummy/config/boot.rb +10 -0
  63. data/spec/dummy/config/database.yml +25 -0
  64. data/spec/dummy/config/environment.rb +5 -0
  65. data/spec/dummy/config/environments/development.rb +37 -0
  66. data/spec/dummy/config/environments/production.rb +67 -0
  67. data/spec/dummy/config/environments/test.rb +37 -0
  68. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  69. data/spec/dummy/config/initializers/inflections.rb +15 -0
  70. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  71. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  72. data/spec/dummy/config/initializers/session_store.rb +8 -0
  73. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/spec/dummy/config/locales/en.yml +5 -0
  75. data/spec/dummy/config/routes.rb +3 -0
  76. data/spec/dummy/db/test.sqlite3 +0 -0
  77. data/spec/dummy/log/test.log +4 -0
  78. data/spec/dummy/public/404.html +26 -0
  79. data/spec/dummy/public/422.html +26 -0
  80. data/spec/dummy/public/500.html +25 -0
  81. data/spec/dummy/public/favicon.ico +0 -0
  82. data/spec/dummy/script/rails +6 -0
  83. data/spec/spec_helper.rb +15 -0
  84. metadata +231 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2009-2012 Ilya N. Zykin, Matthew Clark
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,302 @@
1
+ ### TheSortableTree
2
+
3
+ Engine Based Drag&Drop GUI for awesome_nested_set gem. **Rails >= 3.1**
4
+
5
+ School teacher came to help! TaDa! ;)
6
+
7
+ **Drag&Drop sortable tree**
8
+
9
+ ![TheSortableTree](https://github.com/the-teacher/the_sortable_tree/raw/master/pic.jpg)
10
+
11
+ **Simple nested sets (__min__ option)**
12
+
13
+ ![TheSortableTree](https://github.com/the-teacher/the_sortable_tree/raw/master/pic_min.jpg)
14
+
15
+ **sortable_tree** - recursive helper-method for render sortable awesome_nested_set tree.
16
+
17
+ **sortable_tree** uses partials for rendering, that's why it is **so easy to customize**!
18
+
19
+ ### List of available variants of rendering
20
+
21
+ * Drag&Drop sortable tree
22
+ * Simple nested sets (**min** option)
23
+ * Nested sets with expand/collapse animation (**expand** option) [under development]
24
+ * Nested comments (**comments** option) [under development]
25
+
26
+ ### Can I use gem with Rails 2 or Rails 3.0?
27
+
28
+ Take files from the gem and put it in your rails 2 application.
29
+
30
+ View helper and view files does not depend on the version of rails.
31
+
32
+ Copy and Paste rebuild function from TheSortableTreeController.
33
+
34
+ Perhaps, you may have to slightly change the function of the controller.
35
+
36
+ ### Changelog
37
+
38
+ 1.8.5 - helper can rendering a part tree
39
+
40
+ 1.8.0 - stable release
41
+
42
+ ### Is it fast?
43
+
44
+ Hmmmm...
45
+
46
+ * Development env
47
+ * 584 elements
48
+ * 3 levels deep
49
+
50
+ Rendered by 50 sec.
51
+
52
+ I think it is good result for rendering by partials.
53
+
54
+ Can you makes it faster? Welcome!
55
+
56
+ ### ERB vs HAML vs SLIM
57
+
58
+ So, ERB and SLIM fans want to make gem independent of HAML.
59
+
60
+ Ok, let it be. But you will convert view partials youself. It's my revenge ;)
61
+
62
+ Read project wiki for looking ERB partials
63
+
64
+ **By default I'm use HAML, and now you should define it manually in your Gemfile.**
65
+
66
+ ### Install
67
+
68
+ gem 'haml'
69
+ gem 'awesome_nested_set' # gem 'nested_set'
70
+ gem 'the_sortable_tree'
71
+
72
+ bundle
73
+
74
+ ### Require
75
+
76
+ 1. gem 'nested_set' or gem 'awesome_nested_set'
77
+ 2. gem 'haml'
78
+ 3. JQuery UI
79
+
80
+ ### Example of using with Page Model
81
+
82
+ ### Jquery
83
+
84
+ **app/assets/javascripts/application.js**
85
+
86
+ ``` ruby
87
+ //= require jquery
88
+ //= require jquery-ui
89
+ //= require jquery_ujs
90
+ ```
91
+
92
+ ### Extend your Model
93
+
94
+ ``` ruby
95
+ class Page < ActiveRecord::Base
96
+ include TheSortableTree::Scopes
97
+ # any code here
98
+ end
99
+ ```
100
+
101
+ ### Extend your Controller
102
+
103
+ ``` ruby
104
+ class PagesController < ApplicationController
105
+ include TheSortableTreeController::Rebuild
106
+ # any code here
107
+ end
108
+ ```
109
+
110
+ or (for reversed tree)
111
+
112
+ ``` ruby
113
+ class PagesController < ApplicationController
114
+ include TheSortableTreeController::ReversedRebuild
115
+ # any code here
116
+ end
117
+ ```
118
+
119
+ ### Extend your Routes
120
+
121
+ ``` ruby
122
+ resources :pages do
123
+ collection do
124
+ get :manage
125
+ post :rebuild
126
+ end
127
+ end
128
+ ```
129
+
130
+ **manage** action or any else action for show sortable tree
131
+
132
+ **rebuild** action is _required_ action for correctly work of **the_sortable_tree**
133
+
134
+ ### Find your tree
135
+
136
+ ``` ruby
137
+ class PagesController < ApplicationController
138
+ include TheSortableTreeController::Rebuild
139
+
140
+ def manage
141
+ @pages = Page.nested_set.all
142
+ end
143
+
144
+ # any code here
145
+ end
146
+
147
+ ```
148
+
149
+ or
150
+
151
+ ``` ruby
152
+ class PagesController < ApplicationController
153
+ include TheSortableTreeController::ReversedRebuild
154
+
155
+ def manage
156
+ @pages = Page.reversed_nested_set.all
157
+ end
158
+
159
+ # any code here
160
+ end
161
+ ```
162
+
163
+ ### Render your tree with TheSortableTree (Haml markup)
164
+
165
+ ``` ruby
166
+ - content_for :css do
167
+ = stylesheet_link_tag 'the_sortable_tree', :media => :screen
168
+ - content_for :js do
169
+ = javascript_include_tag 'jquery.ui.nestedSortable'
170
+
171
+ = sortable_tree @pages, :new_url => new_page_path, :max_levels => 4
172
+ ```
173
+
174
+ or (without administrator controls and drag&drop)
175
+
176
+ ``` ruby
177
+ - content_for :css do
178
+ = stylesheet_link_tag 'the_sortable_tree_min', :media => :screen
179
+
180
+ = sortable_tree @pages, :new_url => new_page_path, :path => 'the_sortable_tree_min'
181
+ ```
182
+
183
+ ### Customize tree for User (min version)
184
+
185
+ **Use sortable_tree as view helper for simple rendering of nested_set tree**
186
+
187
+ ``` ruby
188
+ rails g the_sortable_tree:views pages min
189
+ ```
190
+
191
+ It's will generate minimal set of view partials for **sortable_tree** helper
192
+
193
+ ``` ruby
194
+ create app/views/pages/the_sortable_tree_min
195
+ create app/views/pages/the_sortable_tree_min/_children.html.haml
196
+ create app/views/pages/the_sortable_tree_min/_node.html.haml
197
+ create app/views/pages/the_sortable_tree_min/_link.html.haml
198
+ create app/views/pages/the_sortable_tree_min/_tree.html.haml
199
+ ```
200
+
201
+ Just use it or Customize and use it!
202
+
203
+ ``` ruby
204
+ - content_for :css do
205
+ = stylesheet_link_tag 'the_sortable_tree_min', :media => :screen
206
+ = sortable_tree @pages, :new_url => new_page_path, :path => 'pages/the_sortable_tree_min'
207
+ ```
208
+
209
+ ### Customize tree for Administrator (full version)
210
+
211
+ ``` ruby
212
+ rails g the_sortable_tree:views pages
213
+ ```
214
+
215
+ It's will generate view partials for **sortable_tree** helper
216
+
217
+ ``` ruby
218
+ create app/views/pages/the_sortable_tree
219
+ create app/views/pages/the_sortable_tree/_controls.html.haml
220
+ create app/views/pages/the_sortable_tree/_node.html.haml
221
+ create app/views/pages/the_sortable_tree/_js_init_sortable_tree.html.haml
222
+ create app/views/pages/the_sortable_tree/_js_on_update_tree.html.haml
223
+ create app/views/pages/the_sortable_tree/_js_rebuild_ajax.html.haml
224
+ create app/views/pages/the_sortable_tree/_link.html.haml
225
+ create app/views/pages/the_sortable_tree/_children.html.haml
226
+ create app/views/pages/the_sortable_tree/_new.html.haml
227
+ create app/views/pages/the_sortable_tree/_tree.html.haml
228
+ ```
229
+
230
+ Customize and use it!
231
+
232
+ ``` ruby
233
+ - content_for :css do
234
+ = stylesheet_link_tag 'the_sortable_tree', :media => :screen
235
+ - content_for :js do
236
+ = javascript_include_tag 'jquery.ui.nestedSortable'
237
+
238
+ = sortable_tree @pages, :new_url => new_page_path, :path => 'pages/the_sortable_tree', :max_levels => 2
239
+ ```
240
+
241
+ ### Rendering a part of tree
242
+
243
+ ``` ruby
244
+ @root = Page.root
245
+ @pages = @root.descendants.nested_set.all
246
+ ```
247
+
248
+ ``` ruby
249
+ = @root.inspect
250
+ = sortable_tree @pages, :new_url => new_page_path
251
+ ```
252
+
253
+ ### LiveDemo
254
+
255
+ https://github.com/the-teacher/the_sortable_tree_test_app
256
+
257
+ ### Options
258
+
259
+ **id** - id field (:id => :friendly_id etc. **:id** by default)
260
+
261
+ **title** - title field of node (:title => :name etc. **:title** by default)
262
+
263
+ **path** - path to custom view partials (:path => 'pages/the_sortable_tree')
264
+
265
+ **max_levels** - how many draggable levels can be? (**3** by default). **Can't be 0 (zero) and negative**
266
+
267
+ **namespace** - namespace for admin sections for example. (:namespace => :admin, **:namespace** => nil by default)
268
+
269
+ **opts[:level]** - view helper define level of recursion for each node. You can call **opts[:level]** into view partials
270
+
271
+ ### Partials
272
+
273
+ **_tree** - root container for nested set elements
274
+
275
+ **_node** - element of tree (link to current node and nested set of children)
276
+
277
+ **_link** - decoration of link to current element of tree
278
+
279
+ **_children** - decoration of children
280
+
281
+ **_new** - create new element link
282
+
283
+ **_controls** - control elements for current node
284
+
285
+
286
+ **_js_init_sortable_tree** - JS for sortable tree
287
+
288
+ **_js_on_update_tree**- JS for sortable tree
289
+
290
+ **_js_rebuild_ajax**- JS for sortable tree
291
+
292
+ ### Contributors
293
+
294
+ * https://github.com/the-teacher
295
+ * https://github.com/winescout
296
+ * https://github.com/gbrain
297
+ * https://github.com/Mik-die
298
+
299
+ ### Acknowledgments
300
+
301
+ * https://github.com/mjsarfatti/nestedSortable
302
+ * http://iconza.com
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'TheSortableTree'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+
31
+ require 'rspec/rails'
32
+
33
+ RSpec.configure do |config|
34
+ config.use_transactional_fixtures = true
35
+ end
36
+
37
+ task :default => :test
Binary file
@@ -0,0 +1,356 @@
1
+ /*
2
+ * jQuery UI Nested Sortable
3
+ * v 1.3.4 / 28 apr 2011
4
+ * http://mjsarfatti.com/sandbox/nestedSortable
5
+ *
6
+ * Depends:
7
+ * jquery.ui.sortable.js 1.8+
8
+ *
9
+ * License CC BY-SA 3.0
10
+ * Copyright 2010-2011, Manuele J Sarfatti
11
+ */
12
+
13
+ (function($) {
14
+
15
+ $.widget("ui.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
16
+
17
+ options: {
18
+ tabSize: 20,
19
+ disableNesting: 'ui-nestedSortable-no-nesting',
20
+ errorClass: 'ui-nestedSortable-error',
21
+ listType: 'ol',
22
+ maxLevels: 0,
23
+ noJumpFix: 0
24
+ },
25
+
26
+ _create: function(){
27
+ if (this.noJumpFix == false)
28
+ this.element.height(this.element.height());
29
+ this.element.data('sortable', this.element.data('nestedSortable'));
30
+ return $.ui.sortable.prototype._create.apply(this, arguments);
31
+ },
32
+
33
+
34
+
35
+ _mouseDrag: function(event) {
36
+
37
+ //Compute the helpers position
38
+ this.position = this._generatePosition(event);
39
+ this.positionAbs = this._convertPositionTo("absolute");
40
+
41
+ if (!this.lastPositionAbs) {
42
+ this.lastPositionAbs = this.positionAbs;
43
+ }
44
+
45
+ //Do scrolling
46
+ if(this.options.scroll) {
47
+ var o = this.options, scrolled = false;
48
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
49
+
50
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
51
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
52
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
53
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
54
+
55
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
56
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
57
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
58
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
59
+
60
+ } else {
61
+
62
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
63
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
64
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
65
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
66
+
67
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
68
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
69
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
70
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
71
+
72
+ }
73
+
74
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
75
+ $.ui.ddmanager.prepareOffsets(this, event);
76
+ }
77
+
78
+ //Regenerate the absolute position used for position checks
79
+ this.positionAbs = this._convertPositionTo("absolute");
80
+
81
+ //Set the helper position
82
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
83
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
84
+
85
+ //Rearrange
86
+ for (var i = this.items.length - 1; i >= 0; i--) {
87
+
88
+ //Cache variables and intersection, continue if no intersection
89
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
90
+ if (!intersection) continue;
91
+
92
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
93
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
94
+ && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
95
+ && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
96
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
97
+ ) {
98
+
99
+ this.direction = intersection == 1 ? "down" : "up";
100
+
101
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
102
+ this._rearrange(event, item);
103
+ } else {
104
+ break;
105
+ }
106
+
107
+ // Clear emtpy ul's/ol's
108
+ this._clearEmpty(itemElement);
109
+
110
+ this._trigger("change", event, this._uiHash());
111
+ break;
112
+ }
113
+ }
114
+
115
+ var parentItem = (this.placeholder[0].parentNode.parentNode && $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length) ? $(this.placeholder[0].parentNode.parentNode) : null;
116
+ var level = this._getLevel(this.placeholder);
117
+ var childLevels = this._getChildLevels(this.helper);
118
+ var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
119
+ if (previousItem != null) {
120
+ while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0]) {
121
+ if (previousItem[0].previousSibling) {
122
+ previousItem = $(previousItem[0].previousSibling);
123
+ } else {
124
+ previousItem = null;
125
+ break;
126
+ }
127
+ }
128
+ }
129
+
130
+ newList = document.createElement(o.listType);
131
+
132
+ this.beyondMaxLevels = 0;
133
+
134
+ // If the item is moved to the left, send it to its parent level
135
+ if (parentItem != null && this.positionAbs.left < parentItem.offset().left) {
136
+ parentItem.after(this.placeholder[0]);
137
+ this._clearEmpty(parentItem[0]);
138
+ this._trigger("change", event, this._uiHash());
139
+ }
140
+ // If the item is below another one and is moved to the right, make it a children of it
141
+ else if (previousItem != null && this.positionAbs.left > previousItem.offset().left + o.tabSize) {
142
+ this._isAllowed(previousItem, level+childLevels+1);
143
+ if (!previousItem.children(o.listType).length) {
144
+ previousItem[0].appendChild(newList);
145
+ }
146
+ previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
147
+ this._trigger("change", event, this._uiHash());
148
+ }
149
+ else {
150
+ this._isAllowed(parentItem, level+childLevels);
151
+ }
152
+
153
+ //Post events to containers
154
+ this._contactContainers(event);
155
+
156
+ //Interconnect with droppables
157
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
158
+
159
+ //Call callbacks
160
+ this._trigger('sort', event, this._uiHash());
161
+
162
+ this.lastPositionAbs = this.positionAbs;
163
+ return false;
164
+
165
+ },
166
+
167
+ _mouseStop: function(event, noPropagation) {
168
+
169
+ // If the item is in a position not allowed, send it back
170
+ if (this.beyondMaxLevels) {
171
+ var parent = this.placeholder.parent().closest(this.options.items);
172
+
173
+ for (var i = this.beyondMaxLevels - 1; i > 0; i--) {
174
+ parent = parent.parent().closest(this.options.items);
175
+ }
176
+
177
+ this.placeholder.removeClass(this.options.errorClass);
178
+ parent.after(this.placeholder);
179
+ this._trigger("change", event, this._uiHash());
180
+ }
181
+
182
+ $.ui.sortable.prototype._mouseStop.apply(this, arguments);
183
+
184
+ },
185
+
186
+ serialize: function(o) {
187
+
188
+ var items = this._getItemsAsjQuery(o && o.connected);
189
+ var str = []; o = o || {};
190
+
191
+ $(items).each(function() {
192
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
193
+ var pid = ($(o.item || this).parent(o.listType).parent('li').attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
194
+ if(res) str.push((o.key || res[1]+'['+(o.key && o.expression ? res[1] : res[2])+']')+'='+(pid ? (o.key && o.expression ? pid[1] : pid[2]) : 'root'));
195
+ });
196
+
197
+ if(!str.length && o.key) {
198
+ str.push(o.key + '=');
199
+ }
200
+
201
+ return str.join('&');
202
+
203
+ },
204
+
205
+ toHierarchy: function(o) {
206
+
207
+ o = o || {};
208
+ var sDepth = o.startDepthCount || 0;
209
+ var ret = [];
210
+
211
+ $(this.element).children('li').each(function() {
212
+ var level = _recursiveItems($(this));
213
+ ret.push(level);
214
+ });
215
+
216
+ return ret;
217
+
218
+ function _recursiveItems(li) {
219
+ var id = ($(li).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
220
+ if (id != null) {
221
+ var item = {"id" : id[2]};
222
+ if ($(li).children(o.listType).children('li').length > 0) {
223
+ item.children = [];
224
+ $(li).children(o.listType).children('li').each(function () {
225
+ var level = _recursiveItems($(this));
226
+ item.children.push(level);
227
+ });
228
+ }
229
+ return item;
230
+ }
231
+ }
232
+ },
233
+
234
+ toArray: function(o) {
235
+
236
+ o = o || {};
237
+ var sDepth = o.startDepthCount || 0;
238
+ var ret = [];
239
+ var left = 2;
240
+
241
+ ret.push({"item_id": 'root', "parent_id": 'none', "depth": sDepth, "left": '1', "right": ($('li', this.element).length + 1) * 2});
242
+
243
+ $(this.element).children('li').each(function () {
244
+ left = _recursiveArray(this, sDepth + 1, left);
245
+ });
246
+
247
+ function _sortByLeft(a,b) {
248
+ return a['left'] - b['left'];
249
+ }
250
+ ret = ret.sort(_sortByLeft);
251
+
252
+ return ret;
253
+
254
+ function _recursiveArray(item, depth, left) {
255
+
256
+ right = left + 1;
257
+
258
+ if ($(item).children(o.listType).children('li').length > 0) {
259
+ depth ++;
260
+ $(item).children(o.listType).children('li').each(function () {
261
+ right = _recursiveArray($(this), depth, right);
262
+ });
263
+ depth --;
264
+ }
265
+
266
+ id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
267
+
268
+ if (depth === sDepth + 1) pid = 'root';
269
+ else {
270
+ parentItem = ($(item).parent(o.listType).parent('li').attr('id')).match(o.expression || (/(.+)[-=_](.+)/));
271
+ pid = parentItem[2];
272
+ }
273
+
274
+ if (id != null) {
275
+ ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
276
+ }
277
+
278
+ return left = right + 1;
279
+ }
280
+
281
+ },
282
+
283
+ _clear: function(event, noPropagation) {
284
+
285
+ $.ui.sortable.prototype._clear.apply(this, arguments);
286
+
287
+ // Clean last empty ul/ol
288
+ for (var i = this.items.length - 1; i >= 0; i--) {
289
+ var item = this.items[i].item[0];
290
+ this._clearEmpty(item);
291
+ }
292
+ return true;
293
+
294
+ },
295
+
296
+ _clearEmpty: function(item) {
297
+
298
+ if (item.children[1] && item.children[1].children.length == 0) {
299
+ item.removeChild(item.children[1]);
300
+ }
301
+
302
+ },
303
+
304
+ _getLevel: function(item) {
305
+
306
+ var level = 1;
307
+
308
+ if (this.options.listType) {
309
+ var list = item.closest(this.options.listType);
310
+ while (!list.is('.ui-sortable')/* && level < this.options.maxLevels*/) {
311
+ level++;
312
+ list = list.parent().closest(this.options.listType);
313
+ }
314
+ }
315
+
316
+ return level;
317
+ },
318
+
319
+ _getChildLevels: function(parent, depth) {
320
+ var self = this,
321
+ o = this.options,
322
+ result = 0;
323
+ depth = depth || 0;
324
+
325
+ $(parent).children(o.listType).children(o.items).each(function (index, child) {
326
+ result = Math.max(self._getChildLevels(child, depth + 1), result);
327
+ });
328
+
329
+ return depth ? result + 1 : result;
330
+ },
331
+
332
+ _isAllowed: function(parentItem, levels) {
333
+ var o = this.options
334
+ // Are we trying to nest under a no-nest or are we nesting too deep?
335
+ if (parentItem == null || !(parentItem.hasClass(o.disableNesting))) {
336
+ if (o.maxLevels < levels && o.maxLevels != 0) {
337
+ this.placeholder.addClass(o.errorClass);
338
+ this.beyondMaxLevels = levels - o.maxLevels;
339
+ } else {
340
+ this.placeholder.removeClass(o.errorClass);
341
+ this.beyondMaxLevels = 0;
342
+ }
343
+ } else {
344
+ this.placeholder.addClass(o.errorClass);
345
+ if (o.maxLevels < levels && o.maxLevels != 0) {
346
+ this.beyondMaxLevels = levels - o.maxLevels;
347
+ } else {
348
+ this.beyondMaxLevels = 1;
349
+ }
350
+ }
351
+ }
352
+
353
+ }));
354
+
355
+ $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
356
+ })(jQuery);