lolita-menu 0.0.6

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 (42) hide show
  1. data/.document +5 -0
  2. data/Gemfile +15 -0
  3. data/History.rdoc +24 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/menu_items_controller.rb +41 -0
  9. data/app/models/menu.rb +139 -0
  10. data/app/models/menu_item.rb +130 -0
  11. data/app/views/components/lolita/menu/_columns.html.haml +2 -0
  12. data/app/views/components/lolita/menu/_columns_body.html.haml +11 -0
  13. data/app/views/components/lolita/menu/_list.html.haml +11 -0
  14. data/app/views/components/lolita/menu_items/_display.html.haml +7 -0
  15. data/app/views/components/lolita/menu_items/_row.html.haml +10 -0
  16. data/app/views/components/lolita/menu_items/_subtree.html.haml +2 -0
  17. data/config/locales/en.yml +5 -0
  18. data/config/locales/lv.yml +5 -0
  19. data/config/routes.rb +8 -0
  20. data/lib/generators/lolita_menu/assets_generator.rb +13 -0
  21. data/lib/generators/lolita_menu/install_generator.rb +19 -0
  22. data/lib/generators/lolita_menu/templates/migrations/create_menu_items.rb +27 -0
  23. data/lib/generators/lolita_menu/templates/migrations/create_menus.rb +14 -0
  24. data/lib/lolita-menu.rb +13 -0
  25. data/lib/lolita-menu/module.rb +1 -0
  26. data/lib/lolita-menu/rails.rb +5 -0
  27. data/lolita-menu.gemspec +113 -0
  28. data/public/javascripts/jquery-ui-nested-sortables.js +356 -0
  29. data/public/javascripts/lolita/menu-items.js +86 -0
  30. data/public/stylesheets/lolita/menu/style.css +16 -0
  31. data/spec/controllers/menu_items_controller_spec.rb +5 -0
  32. data/spec/models/menu_item_spec.rb +47 -0
  33. data/spec/models/menu_spec.rb +79 -0
  34. data/spec/spec_helper.rb +22 -0
  35. data/test_orm/active_record.rb +9 -0
  36. data/test_orm/boot.rb +9 -0
  37. data/test_orm/config/active_record.yml +2 -0
  38. data/test_orm/db/migrate/01_create_lolita_menus.rb +14 -0
  39. data/test_orm/db/migrate/02_create_lolita_menu_items.rb +27 -0
  40. data/test_orm/rails/config/application.rb +8 -0
  41. data/test_orm/rails/config/enviroment.rb +5 -0
  42. metadata +213 -0
@@ -0,0 +1,2 @@
1
+ = render_component columns,:header
2
+ = render_component "lolita/menu", "columns_body", :columns=>columns,:page=>page
@@ -0,0 +1,11 @@
1
+ %tbody
2
+ - page.each_with_index do |record,index|
3
+ %tr{:class => "#{index % 2 == 0 ? "even" : "odd"} openable-row",:"data-id"=>record.id}
4
+ = render_component columns,:first,:record=>record
5
+ - columns.each do |column|
6
+ %td
7
+ = column.formatter.with(record.send(column.name.to_sym),record).to_s
8
+ = render_component columns,:last,:record=>record
9
+ %tr{:id=>"menu_items_#{record.id}",:class=>"subrow", :style=>"display:none;"}
10
+ %td{:colspan=>columns.size+2}
11
+ = render_component "lolita/menu_items",:display, :menu=>record
@@ -0,0 +1,11 @@
1
+ - content_for :script do
2
+ = javascript_include_tag "lolita/menu-items", "jquery-ui-nested-sortables"
3
+ - content_for :style do
4
+ = stylesheet_link_tag "lolita/menu/style"
5
+ .box
6
+ = render_component list, :title
7
+ = render_component list, :new_resource
8
+ .list
9
+ %table
10
+ = render_component "lolita/menu",:columns,:page=>page,:columns=>list.columns
11
+ = render_component list, :paginator, :page=>page
@@ -0,0 +1,7 @@
1
+ %button{:class => "add_new_menu_item",:"data-id" => menu.id,:"data-url" => menu_items_path(:menu_id=>menu.id)}
2
+ = "Pievienot jaunu"
3
+ %button{:class => "save_menu_tree", :"data-id" => "menu_tree_#{menu.id}"}
4
+ = "Saglabāt izvēlni"
5
+ %ol{:class=>"menu-items-tree", :id=>"menu_tree_#{menu.id}", :"data-url"=>update_tree_menu_items_path(:menu_id=>menu.id)}
6
+ - menu.children.each do |item|
7
+ = render_component "lolita/menu_items", :row, :item=>item
@@ -0,0 +1,10 @@
1
+ %li{:id=>"item-#{item.id}"}
2
+ %div{:class=>"menu-tree-content"}
3
+ %span
4
+ %input{:type=>"text",:value=>item.name,:name=>"name",:"data-id"=>item.id,:"data-url"=>menu_item_path(item.id)}
5
+ %span
6
+ %input{:type=>"text", :value=>item.url,:name=>"url",:class=>"url-input",:"data-id"=>item.id,:"data-url"=>menu_item_path(item.id)}
7
+ %span
8
+ = image_tag("move.png",:style=>"cursor:move;")
9
+ %ol
10
+ = render_component "lolita/menu_items", "subtree", :item => item
@@ -0,0 +1,2 @@
1
+ - item.children.each do |child_item|
2
+ = render_component "lolita/menu_items", "row", :item=>child_item
@@ -0,0 +1,5 @@
1
+ en:
2
+ lolita:
3
+ menu:
4
+ notice: "Menu saved."
5
+ error: "Couldn't save a menu. Try again."
@@ -0,0 +1,5 @@
1
+ lv:
2
+ lolita:
3
+ menu:
4
+ notice: "Izvēlne veiksmīgi saglabāta."
5
+ error: "Izvēlni neizdevās saglabāt. Mēģiniet vēlreiz."
@@ -0,0 +1,8 @@
1
+ Rails.application.routes.draw do
2
+ lolita_for :menus
3
+ resources :menu_items, :only=>[:create,:destroy,:update] do
4
+ collection do
5
+ put :update_tree
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ require 'generators/helpers/file_helper'
2
+ module LolitaMenu
3
+ module Generators
4
+ class AssetsGenerator < Rails::Generators::Base
5
+ include Lolita::Generators::FileHelper
6
+ desc "Copy all from public directory to project public directory."
7
+ def copy_all
8
+ root=File.expand_path("../../../../",__FILE__)
9
+ copy_dir("public",:root=>root)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module LolitaMenu
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include Lolita::Generators::FileHelper
5
+ source_root File.expand_path("../templates", __FILE__)
6
+ desc "Copy assets and create migrations. "
7
+
8
+
9
+ def copy_assets
10
+ generate("lolita_menu:assets")
11
+ end
12
+
13
+ def copy_migration
14
+ copy_file "migrations/create_menus.rb", "db/migrate/#{Time.now.strftime("%Y%m%d%H%M%S")}_create_lolita_menus.rb"
15
+ copy_file "migrations/create_menu_items.rb", "db/migrate/#{(Time.now+1).strftime("%Y%m%d%H%M%S")}_create_lolita_menu_items.rb"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ class CreateLolitaMenuItems < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :lolita_menu_items, :force=>true do |t|
4
+ t.string :name
5
+ t.string :url
6
+ t.integer :menu_id
7
+ t.integer :lft
8
+ t.integer :rgt
9
+ t.integer :depth
10
+ t.integer :parent_id
11
+ t.boolean :is_visible
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :lolita_menu_items, :menu_id
16
+ add_index :lolita_menu_items, [:lft,:rgt,:menu_id,:parent_id]
17
+ add_index :lolita_menu_items, :lft # for sorting
18
+ add_index :lolita_menu_items, :rgt
19
+ add_index :lolita_menu_items, :depth
20
+ add_index :lolita_menu_items, :parent_id
21
+ add_index :lolita_menu_items, :is_visible
22
+ end
23
+
24
+ def self.down
25
+ drop_table :menu_items
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ class CreateLolitaMenus < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :lolita_menus, :force=>true do |t|
4
+ t.string :name
5
+ t.timestamps
6
+ end
7
+
8
+ add_index :lolita_menus,:name
9
+ end
10
+
11
+ def self.down
12
+ drop_table :lolita_menus
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+
2
+ $:<<File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
3
+ require 'haml'
4
+ require 'lolita'
5
+
6
+ module Lolita
7
+ module Menu
8
+
9
+ end
10
+ end
11
+
12
+ require 'lolita-menu/module'
13
+ require 'lolita-menu/rails'
@@ -0,0 +1 @@
1
+ Lolita.add_module Lolita::Menu
@@ -0,0 +1,5 @@
1
+ module LolitaMenu
2
+ class Engine < Rails::Engine
3
+
4
+ end
5
+ end
@@ -0,0 +1,113 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{lolita-menu}
8
+ s.version = "0.0.6"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["ITHouse", "Arturs Meisters"]
12
+ s.date = %q{2011-05-19}
13
+ s.description = %q{Manage public menus for each project inside Lolita.}
14
+ s.email = %q{support@ithouse.lv}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "History.rdoc",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "app/controllers/menu_items_controller.rb",
28
+ "app/models/menu.rb",
29
+ "app/models/menu_item.rb",
30
+ "app/views/components/lolita/menu/_columns.html.haml",
31
+ "app/views/components/lolita/menu/_columns_body.html.haml",
32
+ "app/views/components/lolita/menu/_list.html.haml",
33
+ "app/views/components/lolita/menu_items/_display.html.haml",
34
+ "app/views/components/lolita/menu_items/_row.html.haml",
35
+ "app/views/components/lolita/menu_items/_subtree.html.haml",
36
+ "config/locales/en.yml",
37
+ "config/locales/lv.yml",
38
+ "config/routes.rb",
39
+ "lib/generators/lolita_menu/assets_generator.rb",
40
+ "lib/generators/lolita_menu/install_generator.rb",
41
+ "lib/generators/lolita_menu/templates/migrations/create_menu_items.rb",
42
+ "lib/generators/lolita_menu/templates/migrations/create_menus.rb",
43
+ "lib/lolita-menu.rb",
44
+ "lib/lolita-menu/module.rb",
45
+ "lib/lolita-menu/rails.rb",
46
+ "lolita-menu.gemspec",
47
+ "public/javascripts/jquery-ui-nested-sortables.js",
48
+ "public/javascripts/lolita/menu-items.js",
49
+ "public/stylesheets/lolita/menu/style.css",
50
+ "spec/controllers/menu_items_controller_spec.rb",
51
+ "spec/models/menu_item_spec.rb",
52
+ "spec/models/menu_spec.rb",
53
+ "spec/spec_helper.rb",
54
+ "test_orm/active_record.rb",
55
+ "test_orm/boot.rb",
56
+ "test_orm/config/active_record.yml",
57
+ "test_orm/db/migrate/01_create_lolita_menus.rb",
58
+ "test_orm/db/migrate/02_create_lolita_menu_items.rb",
59
+ "test_orm/rails/config/application.rb",
60
+ "test_orm/rails/config/enviroment.rb"
61
+ ]
62
+ s.homepage = %q{http://github.com/ithouse/lolita-menu}
63
+ s.licenses = ["MIT"]
64
+ s.require_paths = ["lib"]
65
+ s.rubygems_version = %q{1.6.1}
66
+ s.summary = %q{Menu managing plugin for Lolita.}
67
+ s.test_files = [
68
+ "spec/controllers/menu_items_controller_spec.rb",
69
+ "spec/models/menu_item_spec.rb",
70
+ "spec/models/menu_spec.rb",
71
+ "spec/spec_helper.rb"
72
+ ]
73
+
74
+ if s.respond_to? :specification_version then
75
+ s.specification_version = 3
76
+
77
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
78
+ s.add_runtime_dependency(%q<lolita>, ["~> 3.1.6"])
79
+ s.add_runtime_dependency(%q<haml>, [">= 0"])
80
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
81
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
82
+ s.add_development_dependency(%q<rspec-rails>, ["~> 2.6.0"])
83
+ s.add_development_dependency(%q<haml-rails>, [">= 0"])
84
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
85
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
87
+ s.add_development_dependency(%q<rcov>, [">= 0"])
88
+ else
89
+ s.add_dependency(%q<lolita>, ["~> 3.1.6"])
90
+ s.add_dependency(%q<haml>, [">= 0"])
91
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
92
+ s.add_dependency(%q<sqlite3>, [">= 0"])
93
+ s.add_dependency(%q<rspec-rails>, ["~> 2.6.0"])
94
+ s.add_dependency(%q<haml-rails>, [">= 0"])
95
+ s.add_dependency(%q<shoulda>, [">= 0"])
96
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
97
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
98
+ s.add_dependency(%q<rcov>, [">= 0"])
99
+ end
100
+ else
101
+ s.add_dependency(%q<lolita>, ["~> 3.1.6"])
102
+ s.add_dependency(%q<haml>, [">= 0"])
103
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
104
+ s.add_dependency(%q<sqlite3>, [">= 0"])
105
+ s.add_dependency(%q<rspec-rails>, ["~> 2.6.0"])
106
+ s.add_dependency(%q<haml-rails>, [">= 0"])
107
+ s.add_dependency(%q<shoulda>, [">= 0"])
108
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
109
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
110
+ s.add_dependency(%q<rcov>, [">= 0"])
111
+ end
112
+ end
113
+
@@ -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);