navi 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/.gitignore +1 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +3 -0
  4. data/Gemfile +6 -1
  5. data/Gemfile.lock +117 -134
  6. data/README.md +8 -0
  7. data/features/step_definitions/category_steps.rb +1 -1
  8. data/features/step_definitions/page_steps.rb +1 -1
  9. data/features/support/env.rb +7 -2
  10. data/lib/navi.rb +2 -1
  11. data/lib/navi/renderers/base.rb +12 -10
  12. data/lib/navi/renderers/simple_navigation.rb +6 -32
  13. data/lib/navi/renderers/simple_navigation/dynamic_item.rb +18 -0
  14. data/lib/navi/renderers/simple_navigation/dynamic_items.rb +26 -0
  15. data/lib/navi/renderers/simple_navigation/generates_uri.rb +32 -0
  16. data/lib/navi/renderers/simple_navigation/renderer.rb +27 -0
  17. data/lib/navi/version.rb +1 -1
  18. data/navi.gemspec +6 -17
  19. data/spec/dummy/app/assets/javascripts/application.js +1 -1
  20. data/spec/dummy/app/controllers/categories_controller.rb +4 -4
  21. data/spec/dummy/app/controllers/menu_items_controller.rb +11 -4
  22. data/spec/dummy/app/controllers/pages_controller.rb +5 -4
  23. data/spec/dummy/app/views/layouts/application.html.erb +1 -1
  24. data/spec/dummy/config/application.rb +0 -1
  25. data/spec/dummy/config/navigation.rb +0 -0
  26. data/spec/dummy/db/seeds.rb +4 -6
  27. data/spec/dummy/vendor/assets/javascripts/nestedSortable.js +382 -0
  28. data/spec/navi/navi_spec.rb +3 -3
  29. data/spec/navi/navigable_spec.rb +2 -2
  30. data/spec/navi/navigator_spec.rb +7 -7
  31. data/spec/navi/renderers/simple_navigation/dynamic_item_spec.rb +49 -0
  32. data/spec/navi/renderers/simple_navigation/dynamic_items_spec.rb +30 -0
  33. data/spec/navi/renderers/simple_navigation/generates_uri_spec.rb +57 -0
  34. data/spec/navi/renderers/simple_navigation/renderer_spec.rb +41 -0
  35. metadata +229 -142
  36. data/.rvmrc +0 -1
  37. data/spec/navi/renderers/simple_navigation_spec.rb +0 -49
@@ -1,37 +1,11 @@
1
1
  module Navi
2
2
  module Renderers
3
- class SimpleNavigation < Navi::Renderers::Base
4
-
5
- def render(collection, *args)
6
- options = Hash[*args]
7
- options[:expand_all] ||= true
8
- options[:items] ||= create_dynamic_items(collection)
9
- @template.render_navigation options
10
- end
11
-
12
- private
13
-
14
- # Create an array of hashes that can be easily fed into SimpleNavigation
15
- # like in the following like:
16
- # https://github.com/andi/simple-navigation/wiki/Dynamic-Navigation-Items
17
- def create_dynamic_items(collection)
18
- nav = []
19
- collection.each do |nav_item|
20
- item = {}
21
- item[:key] = @template.dom_id(nav_item).to_sym
22
- item[:name] = nav_item.label
23
- if nav_item.url.is_a?(String)
24
- item[:url] = nav_item.url
25
- else # it's a database record then!
26
- item[:url] = @template.polymorphic_path(nav_item.url)
27
- end
28
- item[:options] = {:title => nav_item.title, :class => nav_item.class.name.underscore}
29
- item[:items] = create_dynamic_items(nav_item.children)
30
-
31
- nav << item
32
- end
33
- nav
34
- end
3
+ module SimpleNavigation
35
4
  end
36
5
  end
37
6
  end
7
+
8
+ require 'navi/renderers/simple_navigation/renderer'
9
+ require 'navi/renderers/simple_navigation/generates_uri'
10
+ require 'navi/renderers/simple_navigation/dynamic_items'
11
+ require 'navi/renderers/simple_navigation/dynamic_item'
@@ -0,0 +1,18 @@
1
+ module Navi
2
+ module Renderers
3
+ module SimpleNavigation
4
+ class DynamicItem < Hash
5
+
6
+ def initialize(template, nav_item, options={})
7
+ self[:key] = template.dom_id(nav_item).to_sym
8
+ self[:name] = nav_item.label
9
+ self[:url] = GeneratesUri.execute(template, nav_item, options)
10
+ self[:options] = {title: nav_item.title,
11
+ class: nav_item.class.name.underscore}
12
+ self[:items] = DynamicItems.new(template, nav_item.children, options)
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # Create an array of hashes that can be easily fed into SimpleNavigation
2
+ # like in the following like:
3
+ # https://github.com/andi/simple-navigation/wiki/Dynamic-Navigation-Items
4
+ module Navi
5
+ module Renderers
6
+ module SimpleNavigation
7
+ class DynamicItems < Array
8
+
9
+ def initialize(template, nav_items, options={})
10
+ @template = template
11
+ @options = options
12
+ nav_items.each do |nav_item|
13
+ self << dynamic_item_for(nav_item)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def dynamic_item_for(nav_item)
20
+ DynamicItem.new @template, nav_item, @options
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module Navi
2
+ module Renderers
3
+ module SimpleNavigation
4
+ class GeneratesUri
5
+ easy_class_to_instance
6
+
7
+ def initialize(template, nav_item, options={})
8
+ @template = template
9
+ @nav_item = nav_item
10
+ @namespace = options[:namespace]
11
+ end
12
+
13
+ def execute
14
+ url.is_a?(String) ? url : generated_path
15
+ end
16
+
17
+ private
18
+
19
+ def url
20
+ @nav_item.url
21
+ end
22
+
23
+ def generated_path
24
+ args = [@namespace].flatten.compact
25
+ args << url
26
+ @template.polymorphic_path(args)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ module Navi
2
+ module Renderers
3
+ module SimpleNavigation
4
+ class Renderer < Navi::Renderers::Base
5
+
6
+ def render(collection, *args)
7
+ options = Hash[*args]
8
+ options[:expand_all] ||= true
9
+
10
+ @dynamic_items_options = {}
11
+ @dynamic_items_options[:namespace] = options.delete(:namespace)
12
+
13
+ options[:items] ||= create_dynamic_items(collection)
14
+
15
+ template.render_navigation options
16
+ end
17
+
18
+ private
19
+
20
+ def create_dynamic_items(collection)
21
+ DynamicItems.new(template, collection, @dynamic_items_options)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Navi
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -22,31 +22,20 @@ Gem::Specification.new do |s|
22
22
  s.add_runtime_dependency "activesupport", [">= 3.0.0"]
23
23
  s.add_runtime_dependency "activerecord", [">= 3.0.0"]
24
24
  s.add_runtime_dependency "ordered_tree", [">= 0.1.6"]
25
+ s.add_runtime_dependency 'simple-navigation', ['~> 3.11.0']
26
+ s.add_runtime_dependency 'easy_class_to_instance_method', '0.0.1'
25
27
 
26
- s.add_development_dependency "rspec", ["~> 2.6.0"]
27
- s.add_development_dependency "bundler", "~> 1.0.0"
28
- s.add_development_dependency "simple-navigation", ["3.2.0"]
29
- s.add_development_dependency 'inherited_resources', ["~> 1.3.0"]
30
- s.add_development_dependency 'guard-rspec'
31
- s.add_development_dependency 'libnotify'
32
- s.add_development_dependency 'rb-inotify'
33
28
  s.add_development_dependency 'sqlite3'
34
- s.add_development_dependency 'apotomo', ['~> 1.2.2']
35
29
  s.add_development_dependency 'rails', ['~> 3.2.0']
36
- s.add_development_dependency 'haml'
37
- s.add_development_dependency 'simple-navigation', ['3.2.0']
38
- s.add_development_dependency 'jquery-rails', ['~> 2.0.0']
39
- s.add_development_dependency 'rspec-rails', ['~> 2.6.1']
30
+ s.add_development_dependency 'jquery-rails', ['~> 2.2.1']
31
+ s.add_development_dependency 'rspec-rails', ['~> 2.13.2']
40
32
  s.add_development_dependency 'cucumber-rails', ['0.5.2']
41
33
  s.add_development_dependency 'cucumber'
34
+ s.add_development_dependency 'selenium-webdriver'
42
35
  s.add_development_dependency 'capybara', [">= 1.0.0.rc1"]
43
36
  s.add_development_dependency 'database_cleaner'
44
37
  s.add_development_dependency 'launchy'
45
38
  s.add_development_dependency 'guard-cucumber', ['0.3.4']
46
- s.add_development_dependency 'libnotify'
47
- s.add_development_dependency 'rb-inotify'
48
39
  s.add_development_dependency 'nifty-generators'
49
- s.add_development_dependency 'factory_girl', '~> 2.5.1'
50
- s.add_development_dependency 'ruby-debug19'
40
+ s.add_development_dependency 'factory_girl', '~> 4.2.0'
51
41
  end
52
-
@@ -3,5 +3,5 @@
3
3
  //= require jquery
4
4
  //= require jquery-ui
5
5
  //= require jquery_ujs
6
- //= require 'nestedSortable/jquery.ui.nestedSortable'
6
+ //= require 'nestedSortable'
7
7
  //= require 'menu_items/index'
@@ -1,8 +1,8 @@
1
- class CategoriesController < InheritedResources::Base
1
+ class CategoriesController < ApplicationController
2
+ respond_to :html
2
3
 
3
4
  def create
4
- create! do |success, failure|
5
- success.html {redirect_to categories_url}
6
- end
5
+ @category = Category.create(params[:category])
6
+ respond_with @category, location: categories_path
7
7
  end
8
8
  end
@@ -1,11 +1,18 @@
1
- class MenuItemsController < InheritedResources::Base
1
+ class MenuItemsController < ApplicationController
2
2
  has_widgets do |root|
3
3
  root << widget(:menu_editor)
4
4
  end
5
5
 
6
+ respond_to :html
7
+
8
+ def index
9
+ @menu_items = MenuItem.scoped
10
+ respond_with @menu_items
11
+ end
12
+
6
13
  def update
7
- update! do |success, failure|
8
- success.html {redirect_to menu_items_path}
9
- end
14
+ @menu_item = MenuItem.find(params[:id])
15
+ @menu_item.update_attributes params[:menu_item]
16
+ respond_with @menu_item, location: menu_items_path
10
17
  end
11
18
  end
@@ -1,7 +1,8 @@
1
- class PagesController < InheritedResources::Base
1
+ class PagesController < ApplicationController
2
+ respond_to :html
3
+
2
4
  def create
3
- create! do |success, failure|
4
- success.html {redirect_to pages_path}
5
- end
5
+ @page = Page.create(params[:page])
6
+ respond_with @page, location: pages_path
6
7
  end
7
8
  end
@@ -2,7 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Dummy</title>
5
- <%= stylesheet_link_tag :all %>
5
+ <%= stylesheet_link_tag 'application' %>
6
6
  <%= javascript_include_tag 'application' %>
7
7
  <%= csrf_meta_tag %>
8
8
  <%= yield :header %>
@@ -7,7 +7,6 @@ Bundler.require
7
7
  # Don't know why I had to add these here, but did so because
8
8
  # Rails was complaining these things didn't exist.
9
9
  # Shouldn't Bundler be the one to load these in?
10
- require "inherited_resources"
11
10
  require "apotomo"
12
11
 
13
12
  module Dummy
File without changes
@@ -6,10 +6,8 @@
6
6
  # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
7
  # Mayor.create(:name => 'Daley', :city => cities.first)
8
8
 
9
- require '../../spec/blueprints'
9
+ FactoryGirl.create :page, :name => "Home"
10
+ FactoryGirl.create :page, :name => "About"
10
11
 
11
- Factory :page, :name => "Home"
12
- Factory :page, :name => "About"
13
-
14
- Factory :category, :name => "Cars"
15
- Factory :category, :name => "Animals"
12
+ FactoryGirl.create :category, :name => "Cars"
13
+ FactoryGirl.create :category, :name => "Animals"
@@ -0,0 +1,382 @@
1
+ /*
2
+ * jQuery UI Nested Sortable
3
+ * v 1.3.6 / 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
+ $.widget("ui.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
15
+ options: {
16
+ tabSize: 20,
17
+ disableNesting: 'ui-nestedSortable-no-nesting',
18
+ errorClass: 'ui-nestedSortable-error',
19
+ listType: 'ol',
20
+ maxLevels: 0,
21
+ revertOnError: 1
22
+ },//options
23
+ _create: function() {
24
+ this.element.data('sortable', this.element.data('nestedSortable'));
25
+ return $.ui.sortable.prototype._create.apply(this, arguments);
26
+ },//create
27
+ destroy: function() {
28
+ this.element
29
+ .removeData("nestedSortable")
30
+ .unbind(".nestedSortable");
31
+ return $.ui.sortable.prototype.destroy.apply(this, arguments);
32
+ },//destroy
33
+ _mouseDrag: function(event) {
34
+ //Compute the helpers position
35
+ this.position = this._generatePosition(event);
36
+ this.positionAbs = this._convertPositionTo("absolute");
37
+
38
+ if (!this.lastPositionAbs) {
39
+ this.lastPositionAbs = this.positionAbs;
40
+ }
41
+
42
+ //Do scrolling
43
+ if(this.options.scroll) {
44
+ var o = this.options, scrolled = false;
45
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
46
+
47
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
48
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
49
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
50
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
51
+
52
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
53
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
54
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
55
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
56
+
57
+ } else {
58
+
59
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
60
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
61
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
62
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
63
+
64
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
65
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
66
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
67
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
68
+
69
+ }
70
+
71
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
72
+ $.ui.ddmanager.prepareOffsets(this, event);
73
+ }
74
+
75
+ //Regenerate the absolute position used for position checks
76
+ this.positionAbs = this._convertPositionTo("absolute");
77
+
78
+ //Set the helper position
79
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
80
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
81
+
82
+ //Rearrange
83
+ for (var i = this.items.length - 1; i >= 0; i--) {
84
+
85
+ //Cache variables and intersection, continue if no intersection
86
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
87
+ if (!intersection) continue;
88
+
89
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
90
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
91
+ && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
92
+ && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
93
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
94
+ ) {
95
+
96
+ $(itemElement).mouseenter();
97
+
98
+ this.direction = intersection == 1 ? "down" : "up";
99
+
100
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
101
+ $(itemElement).mouseleave();
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
116
+ && $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
117
+ ? $(this.placeholder[0].parentNode.parentNode)
118
+ : null,
119
+ level = this._getLevel(this.placeholder),
120
+ childLevels = this._getChildLevels(this.helper),
121
+ previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
122
+
123
+ if (previousItem != null) {
124
+ while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0]) {
125
+ if (previousItem[0].previousSibling) {
126
+ previousItem = $(previousItem[0].previousSibling);
127
+ } else {
128
+ previousItem = null;
129
+ break;
130
+ }
131
+ }
132
+ }
133
+
134
+ newList = document.createElement(o.listType);
135
+
136
+ this.beyondMaxLevels = 0;
137
+
138
+ // If the item is moved to the left, send it to its parent level
139
+ if (parentItem != null && this.positionAbs.left < parentItem.offset().left) {
140
+ parentItem.after(this.placeholder[0]);
141
+ this._clearEmpty(parentItem[0]);
142
+ this._trigger("change", event, this._uiHash());
143
+ }
144
+ // If the item is below another one and is moved to the right, make it a children of it
145
+ else if (previousItem != null && this.positionAbs.left > previousItem.offset().left + o.tabSize) {
146
+ this._isAllowed(previousItem, level+childLevels+1);
147
+ if (!previousItem.children(o.listType).length) {
148
+ previousItem[0].appendChild(newList);
149
+ }
150
+ previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
151
+ this._trigger("change", event, this._uiHash());
152
+ }
153
+ else {
154
+ this._isAllowed(parentItem, level+childLevels);
155
+ }
156
+
157
+ //Post events to containers
158
+ this._contactContainers(event);
159
+
160
+ //Interconnect with droppables
161
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
162
+
163
+ //Call callbacks
164
+ this._trigger('sort', event, this._uiHash());
165
+
166
+ this.lastPositionAbs = this.positionAbs;
167
+ return false;
168
+ },//_mouseDrag
169
+ _mouseStop: function(event, noPropagation) {
170
+ // If the item is in a position not allowed, send it back
171
+ if (this.beyondMaxLevels) {
172
+
173
+ this.placeholder.removeClass(this.options.errorClass);
174
+
175
+ if (this.options.revertOnError) {
176
+ if (this.domPosition.prev) {
177
+ $(this.domPosition.prev).after(this.placeholder);
178
+ } else {
179
+ $(this.domPosition.parent).prepend(this.placeholder);
180
+ }
181
+ this._trigger("revert", event, this._uiHash());
182
+ } else {
183
+ var parent = this.placeholder.parent().closest(this.options.items);
184
+
185
+ for (var i = this.beyondMaxLevels - 1; i > 0; i--) {
186
+ parent = parent.parent().closest(this.options.items);
187
+ }
188
+
189
+ parent.after(this.placeholder);
190
+ this._trigger("change", event, this._uiHash());
191
+ }
192
+ }
193
+
194
+ // Clean last empty ul/ol
195
+ for (var i = this.items.length - 1; i >= 0; i--) {
196
+ var item = this.items[i].item[0];
197
+ this._clearEmpty(item);
198
+ }
199
+
200
+ $.ui.sortable.prototype._mouseStop.apply(this, arguments);
201
+ },//_mouseStop
202
+ serialize: function(o) {
203
+ // 'o' is a hash of the options of the plugin (?)
204
+
205
+ // items is an array the dom elements lifted from the page, so we can work on them.
206
+ // It looks something like [li#menu_item_37.menu_item, li#menu_item_38.menu_item, ...]
207
+ var items = this._getItemsAsjQuery(o && o.connected),
208
+ str = []; o = o || {};
209
+
210
+ // Now we go through each of these items
211
+ $(items).each(function() {
212
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '')
213
+ .match(
214
+ o.expression || (/(.+)[-=_](.+)/)),
215
+ pid = ($(o.item || this).parent(o.listType)
216
+ .parent('li')
217
+ .attr(o.attribute || 'id') || '')
218
+ .match(o.expression || (/(.+)[-=_](.+)/));
219
+
220
+ // At this point...
221
+ // 'res' looks like this: ["menu_item_47", "menu_item", "47"]
222
+ // pid looks like this, IF the item (res) has a parent: ["menu_item_46", "menu_item", "46"]
223
+
224
+ // Here we setup the response's parent_id
225
+ if (res) {
226
+ // Updated the following line to have res[0] (instead of res[2]), which will make the itemName be menu_item_47
227
+ itemName = (o.key && o.expression ? res[1] : res[0]);
228
+
229
+ // itemScope looks like this: menu_item[menu_item_37]
230
+ itemScope = o.key || res[1] + '[' + itemName + ']'
231
+
232
+ //create the parentId string, which will look like menu_item[menu_item_37][parent_id]=root
233
+ parentStr = itemScope + '[parent_id]=' + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : 'root')
234
+
235
+ //create the position string, which will look like menu_item[menu_item_37][position]=0
236
+ //this is 0-based position
237
+ positionStr = itemScope + '[position]=' + $(this).index();
238
+
239
+ //push the parentStr and positionStr strings into the str array
240
+ str.push(parentStr);
241
+ str.push(positionStr);
242
+ }
243
+ });
244
+
245
+ if(!str.length && o.key) {
246
+ str.push(o.key + '=');
247
+ }
248
+
249
+ return str.join('&');
250
+ },//serialize
251
+ toHierarchy: function(o) {
252
+ o = o || {};
253
+ var sDepth = o.startDepthCount || 0,
254
+ ret = [];
255
+
256
+ $(this.element).children('li').each(function () {
257
+ var level = _recursiveItems($(this));
258
+ ret.push(level);
259
+ });
260
+
261
+ return ret;
262
+
263
+ function _recursiveItems(li) {
264
+ var id = ($(li).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
265
+ if (id) {
266
+ var item = {"id" : id[2]};
267
+ if ($(li).children(o.listType).children('li').length > 0) {
268
+ item.children = [];
269
+ $(li).children(o.listType).children('li').each(function() {
270
+ var level = _recursiveItems($(this));
271
+ item.children.push(level);
272
+ });
273
+ }
274
+ return item;
275
+ }
276
+ }
277
+ },//toHierarchy
278
+ toArray: function(o) {
279
+ o = o || {};
280
+ var sDepth = o.startDepthCount || 0,
281
+ ret = [],
282
+ left = 2;
283
+
284
+ ret.push({
285
+ "item_id": 'root',
286
+ "parent_id": 'none',
287
+ "depth": sDepth,
288
+ "left": '1',
289
+ "right": ($('li', this.element).length + 1) * 2
290
+ });
291
+
292
+ $(this.element).children('li').each(function () {
293
+ left = _recursiveArray(this, sDepth + 1, left);
294
+ });
295
+
296
+ ret = ret.sort(function(a,b){ return (a.left - b.left); });
297
+
298
+ return ret;
299
+
300
+ function _recursiveArray(item, depth, left) {
301
+ var right = left + 1,
302
+ id,
303
+ pid;
304
+
305
+ if ($(item).children(o.listType).children('li').length > 0) {
306
+ depth ++;
307
+ $(item).children(o.listType).children('li').each(function () {
308
+ right = _recursiveArray($(this), depth, right);
309
+ });
310
+ depth --;
311
+ }
312
+
313
+ id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
314
+
315
+ if (depth === sDepth + 1) {
316
+ pid = 'root';
317
+ } else {
318
+ var parentItem = ($(item).parent(o.listType)
319
+ .parent('li')
320
+ .attr(o.attribute || 'id'))
321
+ .match(o.expression || (/(.+)[-=_](.+)/));
322
+ pid = parentItem[2];
323
+ }
324
+ if (id) {
325
+ ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
326
+ }
327
+ left = right + 1;
328
+ return left;
329
+ } //_recursiveArray
330
+ },//toArray
331
+ _clearEmpty: function(item) {
332
+ var emptyList = $(item).children(this.options.listType);
333
+ if (emptyList.length && !emptyList.children().length) {
334
+ emptyList.remove();
335
+ }
336
+ },//_clearEmpty
337
+ _getLevel: function(item) {
338
+ var level = 1;
339
+ if (this.options.listType) {
340
+ var list = item.closest(this.options.listType);
341
+ while (!list.is('.ui-sortable')) {
342
+ level++;
343
+ list = list.parent().closest(this.options.listType);
344
+ }
345
+ }
346
+ return level;
347
+ }, //_getLevel
348
+ _getChildLevels: function(parent, depth) {
349
+ var self = this,
350
+ o = this.options,
351
+ result = 0;
352
+ depth = depth || 0;
353
+
354
+ $(parent).children(o.listType).children(o.items).each(function (index, child) {
355
+ result = Math.max(self._getChildLevels(child, depth + 1), result);
356
+ });
357
+
358
+ return depth ? result + 1 : result;
359
+ }, //_getChildLevels
360
+ _isAllowed: function(parentItem, levels) {
361
+ var o = this.options;
362
+ // Are we trying to nest under a no-nest or are we nesting too deep?
363
+ if (parentItem == null || !(parentItem.hasClass(o.disableNesting))) {
364
+ if (o.maxLevels < levels && o.maxLevels != 0) {
365
+ this.placeholder.addClass(o.errorClass);
366
+ this.beyondMaxLevels = levels - o.maxLevels;
367
+ } else {
368
+ this.placeholder.removeClass(o.errorClass);
369
+ this.beyondMaxLevels = 0;
370
+ }
371
+ } else {
372
+ this.placeholder.addClass(o.errorClass);
373
+ if (o.maxLevels < levels && o.maxLevels != 0) {
374
+ this.beyondMaxLevels = levels - o.maxLevels;
375
+ } else {
376
+ this.beyondMaxLevels = 1;
377
+ }
378
+ }
379
+ } //_isAllowed
380
+ }));//widget
381
+ $.ui.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.ui.nestedSortable.prototype.options);
382
+ })(jQuery);