navi 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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);