the_sortable_tree_mongoid 1.8.5

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