radiant-drag_order-extension 0.3.9 → 0.4.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.9
1
+ 0.4.0.beta.2
@@ -0,0 +1,2 @@
1
+ %span.handle{:style => "left: #{level * -25}px"}
2
+ = level > 0 ? order_dragger : ""
@@ -0,0 +1,26 @@
1
+ %li.node.page{:class =>"level_#{level}#{children_class}#{virtual_class}", :id => "page_#{page.id}", :'data-page_id' => page.id, :'data-level' => level}
2
+ .attributes
3
+ - render_region :node, :locals => {:page => page, :level => level, :simple => simple} do |node|
4
+ - node.title_column do
5
+ %span.attribute.title
6
+ %span.w1
7
+ - if simple
8
+ = icon
9
+ = node_title
10
+ - else
11
+ = expander(level) + link_to("#{icon} #{node_title}", edit_admin_page_url(page), :title => page.url)
12
+ = page_type
13
+ = spinner
14
+ - node.status_column do
15
+ - unless simple
16
+ %span.attribute.status{:class => "#{page.status.name.downcase}_status", :title => "#{timestamp(page.published_at) if page.published_at}"}= t(page.status.name.downcase)
17
+ - node.add_child_column do
18
+ - unless simple
19
+ %span.attribute.add_child= link_to t('add_child'), new_admin_page_child_url(page)
20
+ - node.remove_column do
21
+ - unless simple
22
+ %span.attribute.remove= link_to t('remove'), remove_admin_page_url(page)
23
+
24
+ %ol.children{:id => "#{level == 0 ? 'pages' : "page_#{page.id}_pages"}"}
25
+ - page.children.each do |child|
26
+ = render_node child, :level => level + 1, :simple => simple
@@ -0,0 +1,20 @@
1
+ - @javascripts << 'admin/dragdrop.js'
2
+ - @javascripts << 'admin/sortable_tree.js'
3
+ - @javascripts << 'admin/extensions/drag_order/drag_order.js'
4
+ - @stylesheets << 'admin/extensions/drag_order/drag_order.css'
5
+
6
+ - @page_title = t('pages') + ' - ' + default_page_title
7
+
8
+ .outset
9
+ = render_region :top
10
+ %ol#site_map.index{:summary=>"Page hierarchy of the current site"}
11
+ - if @homepage
12
+ = render_node @homepage
13
+ - else
14
+ %li.empty= t('no_pages')
15
+ = render_region :bottom
16
+
17
+ - unless @homepage
18
+ #actions
19
+ %ul
20
+ %li= link_to image('plus') + " " + t("new_homepage"), new_admin_page_path
data/config/routes.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
2
 
3
- map.with_options :controller => 'admin/pages' do |page|
4
- page.admin_pages_move_to "admin/pages/:id/move_to/:rel/:pos/:copy", :action => "move_to"
3
+ map.namespace :admin do |admin|
4
+ admin.resources :pages, :collection => { :sort => :put }
5
5
  end
6
6
 
7
7
  end
@@ -8,9 +8,7 @@ class DragOrderExtension < Radiant::Extension
8
8
  StandardTags.send :include, DragOrder::Tags::Core
9
9
  Admin::PagesController.send :include, DragOrder::Controllers::Admin::PagesController
10
10
 
11
- admin.pages.index.add :sitemap_head, "drag_order_header", :before => "title_column_header"
12
- admin.pages.index.add :node, "drag_order", :before => "title_column"
13
- admin.pages.index.add :top, "top"
11
+ admin.pages.index.add :node, "handle", :before => "title_column"
14
12
  end
15
13
 
16
14
  end
@@ -8,141 +8,30 @@ module DragOrder
8
8
 
9
9
  helper_method :order_dragger
10
10
 
11
- def move_to
12
- @page = Page.find(params[:id])
13
- @old_parent = @page.parent
14
- @current_position = params[:pos].to_i
15
-
16
- ensure_no_nil_position_values
17
-
18
- remove_page_from_old_position unless copying?
19
-
20
- @target = Page.find(params[:rel])
21
-
22
- make_room_for_page if @current_position != 2
23
-
24
- if copying?
25
- @orig_parts = @page.parts
26
- @page = @page.clone
27
- end
28
-
29
- @target.reload
30
-
31
- put_page
32
-
33
- solve_slug_conflicts if copying? || new_parent_different?
34
-
35
- @page.save!
36
-
37
- create_copy_of_parts if copying?
38
-
39
- clear_cache
40
- redirect_back_or_to_admin_pages_page
41
- end
42
-
43
- private
44
- def order_dragger
45
- %{<img src="/images/admin/extensions/drag_order/handle.png" alt ="Drag this icon to move the page" />}
46
- end
47
-
48
- def copying?
49
- params[:copy].to_i > 0
50
- end
51
-
52
- def ensure_no_nil_position_values
53
- if @page.newly_created_siblings?
54
- i = 1
55
- @page.siblings_and_self.each do |p|
56
- p.position = i
57
- p.save
58
- i += 1
11
+ def sort
12
+ begin
13
+ parent = Page.find(params[:parent_id])
14
+ params[:children].split(',').each_with_index do |child,index|
15
+ Page.find(child).update_attributes!(
16
+ :position => index,
17
+ :parent_id => params[:parent_id]
18
+ )
59
19
  end
60
- @page.reload
61
- end
62
- end
63
-
64
- def remove_page_from_old_position
65
- @page.following_siblings.each do |sibling|
66
- sibling.position -= 1
67
- sibling.save!
68
- end
69
- end
70
-
71
- def put_page
72
- if @current_position != 2
73
- @page.parent = @target.parent
74
- @page.position = @target.position.to_i + (@current_position == 1 ? 1 : -1)
75
- else
76
- @page.parent = @target
77
- @page.position = 1
78
- end
79
- end
80
-
81
- def make_room_for_page
82
- new_siblings = Page.children_of_after_position(@target.parent_id, @target.position + @current_position)
83
- new_siblings.each do |sibling|
84
- if sibling != @page || copying?
85
- sibling.position += 1
86
- sibling.save!
20
+
21
+ respond_to do |format|
22
+ format.js { render :text => 'Pages successfully sorted.' }
87
23
  end
88
- end
89
- end
90
-
91
- def new_parent_different?
92
- # @page.parent.changed? always gives false...
93
- @page.parent != @old_parent
94
- end
95
-
96
- def solve_slug_conflicts
97
- check_slug = @page.slug.sub(/-copy-?[0-9]*$/, "")
98
- count = 0
99
- parent_id = @current_position == 2 ? @target.id : @target.parent.id
100
- duplicates = Page.children_of_with_slug_like(parent_id, check_slug )
101
- duplicates.each do |d|
102
- m = d.slug.match("^#{check_slug}(-copy-?([0-9]*))?$")
103
- if !m.nil?
104
- if !(m[2].nil? || m[2] == "")
105
- nc = m[2].to_i + 1
106
- elsif m[1]
107
- nc = 2
108
- else
109
- nc = 1
110
- end
111
- count = nc if nc > count
24
+ rescue Exception => e # Without this resource controller will exception when it looks for Page.find('sort.js')
25
+ respond_to do |format|
26
+ format.js { render :text => 'Could not sort Pages.', :status => :unprocessable_entity }
112
27
  end
113
28
  end
114
- if count > 0
115
- # Remove old copy counters
116
- re = / - COPY ?[0-9]*$/
117
- @page.title.sub! re, ""
118
- @page.breadcrumb.sub! re, ""
119
- # Add new copy counters
120
- @page.slug = check_slug + "-copy" + (count > 1 ? "-" + count.to_s : "")
121
- @page.title += " - COPY" + (count > 1 ? " " + count.to_s : "")
122
- @page.breadcrumb += " - COPY" + (count > 1 ? " " + count.to_s : "")
123
- end
124
29
  end
125
30
 
126
- def create_copy_of_parts
127
- @orig_parts.each do |op|
128
- @page.parts.create({
129
- :name => op.name,
130
- :filter_id => op.filter_id,
131
- :content => op.content
132
- })
133
- end
134
- end
135
-
136
- def redirect_back_or_to_admin_pages_page
137
- redirect_to(:back) rescue redirect_to(admin_page_url)
138
- end
31
+ private
139
32
 
140
- def clear_cache
141
- if defined? ResponseCache == 'constant'
142
- ResponseCache.instance.clear
143
- else
144
- Radiant::Cache.clear
145
- end
33
+ def order_dragger
34
+ %{<img src="/images/admin/extensions/drag_order/handle.png" alt ="Drag this icon to move the page" />}
146
35
  end
147
36
 
148
37
  end
@@ -13,30 +13,6 @@ module DragOrder
13
13
 
14
14
  self.reflections[:children].options[:order] = "position ASC"
15
15
 
16
- class << self
17
-
18
- def children_of_after_position(parent, position)
19
- find_all_by_parent_id(parent, :conditions => [ 'position >= ?', position ])
20
- end
21
-
22
- def children_of_with_slug_like(parent, slug)
23
- find_all_by_parent_id(parent, :conditions => [ 'slug LIKE ?', slug ])
24
- end
25
-
26
- end
27
-
28
- def newly_created_siblings?
29
- self.class.find_all_by_parent_id(parent_id, :conditions => ["position is null"] ).size > 0
30
- end
31
-
32
- def siblings_and_self
33
- self.class.find_all_by_parent_id(parent_id, :order => ["position ASC"] )
34
- end
35
-
36
- def following_siblings
37
- self.class.find_all_by_parent_id(parent_id, :conditions => [ 'position > ?', position ] )
38
- end
39
-
40
16
  private
41
17
  def set_initial_position
42
18
  self.position ||= begin
@@ -1,192 +1,63 @@
1
- var DragOrder = Class.create({
2
-
3
- // Constants
4
- BEFORE : 0,
5
- AFTER : 1,
6
- CHILD : 2,
7
- CHILD_PAD : 21,
8
- NO_COPY : 0,
9
- COPY : 1,
10
-
11
- // Defaults
12
- origRow : null,
13
- expandObj : null,
14
- rowHeight : 0,
15
- dragLine : null,
16
- moveTo : null,
17
-
18
- initialize: function(table) {
19
- // Needed in order to use SiteMap function. Already created in sitemap.js,
20
- // but cannot be referred to...
21
- this.sMap = SiteMapBehavior.attach(new Element('table'));
22
-
23
- // Attach listeners to drag images in table
24
- var _this = this;
25
- Event.observe( $(table), 'mousedown', function(evt){
26
- if ($(evt.target.parentNode).hasClassName('drag_order') && evt.target.tagName.toLowerCase() == 'img')
27
- _this.rowDragStart(evt);
28
- }.bindAsEventListener(this) );
29
- document.dragOrderObj = this;
30
- },
1
+ document.observe("dom:loaded", function() {
2
+ drag_order_index = new DragOrderIndex();
3
+ drag_order_index.initialize();
4
+ })
5
+
6
+ var DragOrderIndex = Class.create({
31
7
 
32
- rowDragStart: function(evt) {
33
- // Store original row, give it a color, store the height of the row and initialise some objects
34
- this.origRow = evt.findElement('tr');
35
- this.origRow.addClassName('dragging');
36
- this.rowHeight = this.origRow.getHeight();
37
- this.moveTo = new Object();
38
- this.expandObj = new Object();
39
- this.childEdge = $(evt.target).cumulativeOffset().left + $(evt.target).getWidth();
40
-
41
- // Attach event listeners for the movement
42
- this.moveBind = this.rowDragMove.bindAsEventListener(this);
43
- Event.observe($(document.body), 'mousemove', this.moveBind);
44
- this.stopBind = this.rowDragStop.bindAsEventListener(this);
45
- Event.observe($(document.body), 'mouseup', this.stopBind);
46
-
47
- return this.cancelEvent(evt);
8
+ initialize: function() {
9
+ this.sortPages();
48
10
  },
49
11
 
50
- rowDragMove: function(evt) {
51
- // If no origRow is available, we've come here by mistake
52
- if (!this.origRow) return false;
53
-
54
- // Create dragline
55
- if (!this.dragLine) {
56
- this.dragLine = new Element('div');
57
- this.dragLine.id = 'drag_line';
58
- this.dragLine.setStyle({
59
- position: 'absolute'
60
- });
61
- this.dragLine.innerHTML = "line";
62
- dragLineCircle = new Element('div');
63
- this.dragLine.appendChild(dragLineCircle);
64
- document.body.appendChild(this.dragLine);
65
- }
66
- else
67
- this.dragLine.show();
68
-
69
- // If children are visible, hide them first
70
- if (this.sMap.isExpanded(this.origRow))
71
- this.sMap.hideBranch(this.origRow, this.origRow.getElementsByClassName('expander')[0] );
72
-
73
- // Loop through all rows
74
- var _this = document.dragOrderObj;
75
- var top;
76
- this.origRow.parentNode.getElementsBySelector('tr').find(function(obj){
77
- top = obj.cumulativeOffset().top;
78
-
79
- // Check if cursor is over row
80
- if (evt.pageY >= top && evt.pageY <= top + _this.rowHeight) {
81
-
82
- // If row has children and is collapsed, create timer for expansion
83
- if (obj.hasClassName('children_hidden') && _this.expandObj != obj) {
84
- _this.expandObj.row = obj;
85
- if (_this.expandObj.timer)
86
- clearTimeout(_this.expandObj.timer);
87
- _this.expandObj.timer = setTimeout("document.dragOrderObj.rowDragExpand();", 750);
88
- }
89
- else if (_this.expandObj != obj) {
90
- _this.expandObj.row = null;
91
- clearTimeout(_this.expandObj.timer);
92
- }
93
-
94
- var targetRow = null;
95
- var targetLoc;
96
- // If on the upper half of the row, put the dragline at the top of the row (= bottom of previous)
97
- if (evt.pageY >= top && evt.pageY <= top + _this.rowHeight / 2 && obj.previous()) {
98
- if (obj.previous().hasClassName('children_visible')) {
99
- targetRow = obj;
100
- targetLoc = _this.BEFORE;
101
- }
102
- else {
103
- targetRow = _this.sMap.extractLevel(obj.previous()) > _this.sMap.extractLevel(obj) ? obj : obj.previous();
104
- targetLoc = _this.sMap.extractLevel(obj.previous()) > _this.sMap.extractLevel(obj) ? _this.BEFORE : _this.AFTER;
105
- }
106
- }
107
- // If on the lower half of the row, put the line at the bottom of the row
108
- else if (evt.pageY > top + _this.rowHeight / 2 && evt.pageY <= top + _this.rowHeight) {
109
- // Check for moving as new child
110
- if (obj != _this.origRow && !_this.sMap.hasChildren(obj) && evt.pageX > _this.childEdge) {
111
- targetRow = obj;
112
- targetLoc = _this.CHILD;
113
- }
114
- else {
115
- targetRow = obj.hasClassName('children_visible') ? obj.next() : obj;
116
- targetLoc = obj.hasClassName('children_visible') ? _this.BEFORE : _this.AFTER;
117
- }
12
+ sortPages: function() {
13
+ var tree = new SortableTree('pages', {
14
+ draggable: {
15
+ handle: 'handle',
16
+ ghosting: false,
17
+ constraint: false,
18
+ onDrag: function(drag, event) {
19
+ drag.element.addClassName('drag_move');
20
+ },
21
+ onEnd: function(drag, event) {
22
+ drag.element.removeClassName('drag_move');
118
23
  }
24
+ },
25
+ onDrop: function(drag, drop, event){
26
+ var parent = drag.element.up('.page')
27
+ var children = '';
28
+
29
+ this.moveHandles(drag.element);
119
30
 
120
- // Check for copy action
121
- var copy = evt.ctrlKey || evt.metaKey ? true : false;
122
- if (copy)
123
- _this.dragLine.getElementsByTagName('div')[0].addClassName('copy');
124
- else
125
- _this.dragLine.getElementsByTagName('div')[0].removeClassName('copy');
126
- }
127
-
128
- // If a row has been found
129
- if (targetRow) {
130
- // Set the dragline
131
- var padding = parseInt(targetRow.firstDescendant().getStyle('padding-left')) + 30;
132
- _this.dragLine.style.width = targetRow.getWidth() - padding - (targetLoc == _this.CHILD ? _this.CHILD_PAD : 0) + 'px';
133
- _this.dragLine.setStyle({
134
- left: targetRow.cumulativeOffset().left + padding + (targetLoc == _this.CHILD ? _this.CHILD_PAD : 0) + 'px',
135
- top: targetRow.cumulativeOffset().top + (targetLoc == _this.AFTER || targetLoc == _this.CHILD ? _this.rowHeight : 0) - 1 + 'px'
31
+ parent.down('.children').immediateDescendants().each(function(page) {
32
+ children += page.readAttribute('data-page_id') + ','
136
33
  });
34
+ children = children.slice(0,-1);
137
35
 
138
- // Store the found row and options
139
- _this.moveTo.hovering = obj;
140
- _this.moveTo.relativeTo = targetRow;
141
- _this.moveTo.side = targetLoc;
142
- _this.moveTo.copy = copy;
36
+ new Ajax.Request('/admin/pages/sort.js', {
37
+ method: 'put',
38
+ parameters: {
39
+ 'parent_id': parent.readAttribute('data-page_id'),
40
+ 'children' : children
41
+ }
42
+ })
143
43
 
144
- return true;
145
- }
146
-
44
+ }.bind(this)
147
45
  });
148
-
149
- return this.cancelEvent(evt);
150
- },
151
-
152
- rowDragExpand: function(row) {
153
- row = this.expandObj.row;
154
- this.sMap.showBranch(row, row.getElementsByClassName('expander')[0] );
155
- },
156
-
157
- rowDragStop: function() {
158
-
159
- if (this.moveTo.relativeTo && (this.moveTo.hovering != this.origRow || this.moveTo.copy))
160
- window.location.href = "/admin/pages/" + this.sMap.extractPageId(this.origRow) + "/move_to/" + this.sMap.extractPageId(this.moveTo.relativeTo) + "/" + this.moveTo.side + "/" + (this.moveTo.copy ? this.COPY : this.NO_COPY);
161
- else {
162
- // Cleanup not necessary when redirected
163
- this.origRow.removeClassName('dragging');
164
-
165
- this.origRow = null;
166
- if (this.expandObj.timer) clearTimeout(this.expandObj.timer);
167
- this.expandObj = null;
168
- if (this.dragLine) this.dragLine.hide();
169
- }
170
-
171
- Event.stopObserving(document.body, 'mousemove', this.moveBind);
172
- Event.stopObserving(document.body, 'mouseup', this.stopBind);
46
+ tree.setSortable();
173
47
  },
174
48
 
175
- cancelEvent: function(evt) {
176
- // Cancel default event actions
177
- evt.returnValue = false;
178
- evt.cancel = true;
179
- if (evt.preventDefault) evt.preventDefault();
180
- return false;
49
+ moveHandles: function(page) {
50
+ var level = parseInt(page.up('.page').getAttribute('data-level'))
51
+ level += 1;
52
+ page.setAttribute('data-level', level)
53
+ page.down('.handle').setStyle({ left: (level * -25) + 'px' })
54
+
55
+ page.select('.children').each(function(container) {
56
+ level += 1
57
+ container.select('.page').each(function(child) {
58
+ child.setAttribute('data-level', level)
59
+ child.down('.handle').setStyle({ left: (level * -25) + 'px' })
60
+ });
61
+ });
181
62
  }
182
-
183
- });
184
-
185
- // If the DOM is loaded, create the DragOrder object
186
- document.observe('dom:loaded', function() {
187
- $$('table.index').each(function(table){
188
- if(table.identify() == 'pages' || table.identify() == 'site_map') {
189
- new DragOrder(table);
190
- }
191
- });
192
- });
63
+ })
@@ -0,0 +1,111 @@
1
+ /*
2
+ * sitemap.js
3
+ *
4
+ * depends on: prototype.js and lowpro.js
5
+ *
6
+ * Used by Radiant to create the expandable sitemap.
7
+ *
8
+ * To use, simply add the following lines to application.js:
9
+ *
10
+ * Event.addBehavior({
11
+ * 'table#site_map': SiteMapBehavior()
12
+ * });
13
+ *
14
+ */
15
+
16
+ var SiteMapBehavior = Behavior.create({
17
+ initialize: function() {
18
+ this.readExpandedCookie();
19
+ },
20
+
21
+ onclick: function(event) {
22
+ if (this.isExpander(event.target)) {
23
+ var row = event.findElement('li');
24
+ if (this.hasChildren(row)) {
25
+ this.toggleBranch(row, event.target);
26
+ }
27
+ }
28
+ },
29
+
30
+ hasChildren: function(row) {
31
+ return !row.hasClassName('no_children');
32
+ },
33
+
34
+ isExpander: function(element) {
35
+ return element.match('img.expander');
36
+ },
37
+
38
+ isExpanded: function(row) {
39
+ return row.hasClassName('children_visible');
40
+ },
41
+
42
+ isRow: function(element) {
43
+ return element && element.tagName && element.match('li');
44
+ },
45
+
46
+ extractPageId: function(row) {
47
+ return row.readAttribute('data-page_id').toInteger();
48
+ },
49
+
50
+ getExpanderImageForRow: function(row) {
51
+ return row.down('img');
52
+ },
53
+
54
+ readExpandedCookie: function() {
55
+ var matches = document.cookie.match(/expanded_rows=(.+?)(;|$)/);
56
+ this.expandedRows = matches ? decodeURIComponent(matches[1]).split(',') : [];
57
+ },
58
+
59
+ saveExpandedCookie: function() {
60
+ document.cookie = "expanded_rows=" + encodeURIComponent(this.expandedRows.uniq().join(",")) + "; path=/admin";
61
+ },
62
+
63
+ persistCollapsed: function(row) {
64
+ var pageId = this.extractPageId(row);
65
+ this.expandedRows = this.expandedRows.without(pageId);
66
+ this.saveExpandedCookie();
67
+ },
68
+
69
+ persistExpanded: function(row) {
70
+ var pageId = this.extractPageId(row);
71
+ this.expandedRows.push(pageId);
72
+ this.saveExpandedCookie();
73
+ },
74
+
75
+ toggleExpanded: function(row, img) {
76
+ if (!img) img = this.getExpanderImageForRow(row);
77
+ if (this.isExpanded(row)) {
78
+ img.src = img.src.replace('collapse', 'expand');
79
+ row.removeClassName('children_visible');
80
+ row.addClassName('children_hidden');
81
+ this.persistCollapsed(row);
82
+ } else {
83
+ img.src = img.src.replace('expand', 'collapse');
84
+ row.removeClassName('children_hidden');
85
+ row.addClassName('children_visible');
86
+ this.persistExpanded(row);
87
+ }
88
+ },
89
+
90
+ hideBranch: function(parent, img) {
91
+ this.toggleExpanded(parent, img);
92
+ },
93
+
94
+ showBranch: function(parent, img) {
95
+ this.toggleExpanded(parent, img);
96
+ },
97
+
98
+ toggleBranch: function(row, img) {
99
+ if (!this.updating) {
100
+ if (this.isExpanded(row)) {
101
+ this.hideBranch(row, img);
102
+ } else {
103
+ this.showBranch(row, img);
104
+ }
105
+ }
106
+ }
107
+ });
108
+
109
+ Event.addBehavior({
110
+ '#site_map': SiteMapBehavior()
111
+ })
@@ -0,0 +1,223 @@
1
+ var SortableTree = Class.create({
2
+ initialize: function(element, options) {
3
+ this.element = $(element);
4
+ this.root = new SortableTree.Node(this, null, element, options);
5
+ this.isSortable = false;
6
+ },
7
+
8
+ toggleSortable: function() {
9
+ this.isSortable ? this.setUnsortable() : this.setSortable();
10
+ },
11
+
12
+ setSortable: function() {
13
+ Element.addClassName(this.root.element, 'sortable');
14
+ this.root.setSortable();
15
+ this.isSortable = true;
16
+ },
17
+
18
+ setUnsortable: function() {
19
+ Element.removeClassName(this.root.element, 'sortable');
20
+ this.root.setUnsortable();
21
+ this.isSortable = false;
22
+ },
23
+
24
+ find: function(element) {
25
+ return this.root.find($(element));
26
+ },
27
+
28
+ unmark_all: function() {
29
+ this.root.unmark();
30
+ }
31
+ });
32
+
33
+ SortableTree.Node = Class.create({
34
+ initialize: function(tree, parent, element, options) {
35
+ this.tree = tree;
36
+ this.parent = parent;
37
+ this.element = $(element);
38
+
39
+ this.options = Object.extend({
40
+ tagName: 'LI',
41
+ containerTagName: 'OL',
42
+ droppable: {},
43
+ draggable: {}
44
+ }, options || {});
45
+
46
+ this.droppable_options = Object.extend({
47
+ onHover: function(drag, drop, overlap){ this.onHover(drag, drop, overlap); }.bind(this),
48
+ onDrop: function(drag, drop, event){ this.onDrop(drag, drop, event); }.bind(this),
49
+ overlap: 'vertical',
50
+ hoverclass: 'drop_hover'
51
+ }, options.droppable);
52
+
53
+ this.draggable_options = Object.extend({
54
+ ghosting: true,
55
+ revert: true,
56
+ constraint: 'vertical',
57
+ reverteffect: function(element, top_offset, left_offset) {
58
+ element.setStyle({left: '0px', top: '0px'});
59
+ // would be so cool to be able to use this. but it leaves a backgroundColor
60
+ // style property on the element which overwrites the class' value
61
+ // (i.e. the drop marker) and apperently can't be removed anymore (?)
62
+ // new Effect.Highlight(element, { startcolor: '#FFFF99' })
63
+ }
64
+ }, options.draggable);
65
+
66
+ this.initChildren();
67
+ },
68
+
69
+ id: function() {
70
+ if (!this._id) {
71
+ var match = this.element.id.match(/^[\w]+_([\d]*)$/);
72
+ this._id = encodeURIComponent(match ? match[1] : null);
73
+ }
74
+ return this._id;
75
+ },
76
+
77
+ previousSibling: function() {
78
+ var pos = this.parent.children.indexOf(this);
79
+ return pos > 0 ? this.parent.children[pos - 1] : null;
80
+ },
81
+
82
+ initChildren: function() {
83
+ this.children = [];
84
+ var container = this.findContainer(this.element);
85
+ if(container){
86
+ $A(container.childNodes).each(function(child) {
87
+ if(this.acceptTagName(child)) {
88
+ this.children.push(new SortableTree.Node(this.tree, this, child, this.options));
89
+ }
90
+ }.bind(this));
91
+ }
92
+ },
93
+
94
+ acceptTagName: function(element) {
95
+ return element.tagName && element.tagName.toUpperCase() == this.options.tagName;
96
+ },
97
+
98
+ setSortable: function() {
99
+ Droppables.add(this.element, this.droppable_options);
100
+ this.draggable = new Draggable(this.element, this.draggable_options);
101
+ this.children.each(function(child) { child.setSortable(); });
102
+ },
103
+
104
+ setUnsortable: function() {
105
+ Droppables.remove(this.element);
106
+ this.draggable.destroy();
107
+ this.children.each(function(child) { child.setUnsortable(); });
108
+ },
109
+
110
+ find: function(element) {
111
+ if(element == this.element) return this;
112
+ for(var i = 0; i < this.children.length; i++) {
113
+ var node = this.children[i].find(element);
114
+ if(node) return node;
115
+ }
116
+ },
117
+
118
+ findContainer: function(element) {
119
+ if(element.tagName != this.options.containerTagName) {
120
+ element = $A(element.childNodes).detect(function(node) {
121
+ return node.tagName == this.options.containerTagName;
122
+ }.bind(this));
123
+ }
124
+ return element;
125
+ },
126
+
127
+ findOrCreateContainer: function(element) {
128
+ var container = this.findContainer(element);
129
+ if(!container) {
130
+ container = document.createElement(this.options.containerTagName);
131
+ element.appendChild(container);
132
+ }
133
+ return container;
134
+ },
135
+
136
+ onHover: function(drag, drop, overlap) {
137
+ if(this.canContainChildren(drop)) {
138
+ this.dropPosition = overlap < 0.15 ? 'bottom' : overlap > 0.85 ? 'top' : 'insert';
139
+ } else {
140
+ this.dropPosition = overlap < 0.5 ? 'bottom' : 'top';
141
+ }
142
+ this.mark(drop);
143
+ // $('log').update('hovering: ' + drop.tagName + ': ' + drop.id + "<br />" +
144
+ // 'classes: ' + drop.className + "<br />" +
145
+ // 'dropPosition: ' + this.dropPosition)
146
+ },
147
+
148
+ canContainChildren: function(element) {
149
+ if(this.options.droppable.container) {
150
+ return element.match(this.options.droppable.container);
151
+ }
152
+ return true;
153
+ },
154
+
155
+ onDrop: function(drag, drop, event) {
156
+ drag = this.tree.find(drag);
157
+ drop = this.tree.find(drop);
158
+
159
+ // i.e. don't do anything if it's a toplevel node and has been dropped on "itself"
160
+ // another way around this could be to change scriptaculous to affect() a node
161
+ // when it has been dropped on itself
162
+ if(drop.parent || this.dropPosition == 'insert') {
163
+ switch(this.dropPosition) {
164
+ case 'top': drop.parent.insertBefore(drag, drop); break;
165
+ case 'bottom': drop.parent.insertBefore(drag, drop.nextSibling()); break;
166
+ case 'insert': this.insertBefore(drag, this.firstChild()); break;
167
+ }
168
+ }
169
+
170
+ if(this.options.onDrop) this.options.onDrop(drag, drop, event);
171
+
172
+ this.tree.unmark_all();
173
+ },
174
+
175
+ mark: function(element, position) {
176
+ this.tree.unmark_all();
177
+ Element.addClassName(element, 'drop_' + this.dropPosition);
178
+ },
179
+
180
+ unmark: function() {
181
+ ['drop_top', 'drop_bottom', 'drop_insert'].each(function(classname){
182
+ Element.removeClassName(this.element, classname);
183
+ }.bind(this));
184
+ this.children.each(function(child) { child.unmark(); });
185
+ },
186
+
187
+ to_params: function(name) {
188
+ name = name || this.tree.element.id;
189
+ var leftNode = this.previousSibling();
190
+ return name + '[' + this.id() + '][parent_id]=' + this.parent.id() + '&' +
191
+ name + '[' + this.id() + '][left_id]=' + (leftNode ? leftNode.id() : ''); // null
192
+ },
193
+
194
+ firstChild: function() {
195
+ return this.children.length > 0 ? this.children[0] : null;
196
+ },
197
+
198
+ previousSibling: function() {
199
+ var pos = this.parent.children.indexOf(this);
200
+ return pos > 0 ? this.parent.children[pos - 1] : null;
201
+ },
202
+
203
+ nextSibling: function() {
204
+ var pos = this.parent.children.indexOf(this);
205
+ return pos + 1 < this.parent.children.length ? this.parent.children[pos + 1] : null;
206
+ },
207
+
208
+ removeChild: function(node) {
209
+ this.children.splice(this.children.indexOf(node), 1);
210
+ node.element.parentNode.removeChild(node.element);
211
+ },
212
+
213
+ insertBefore: function(node, sibling) {
214
+ if(node == sibling) return;
215
+
216
+ node.parent.removeChild(node);
217
+ node.parent = this;
218
+ var pos = sibling ? this.children.indexOf(sibling) : this.children.length;
219
+ this.children.splice(pos, 0, node);
220
+
221
+ this.findOrCreateContainer(this.element).insertBefore(node.element, sibling ? sibling.element : null);
222
+ }
223
+ });
@@ -1,40 +1,120 @@
1
- #content
2
-
3
- table.index
1
+ #content
2
+
3
+ #pages
4
+ padding-left: 7px
4
5
 
5
- tr.dragging
6
- background-color: #D3ECF4
6
+ .page
7
+ position: relative
8
+
9
+ &.drop_hover
10
+ background-color: #F9F9F9
7
11
 
8
- td
9
- color: #FFFFFF
12
+ &.drop_insert
13
+ background-color: #DFEBFF
14
+
15
+ &.drag_move
16
+ .children
17
+ opacity: 0.25
18
+
19
+ .attributes
20
+ width: 100%
21
+ height: 31px
22
+ line-height: 31px
23
+ padding: 4px 0 4px 10px
24
+ overflow: hidden
25
+ text-decoration: none
26
+ padding-left: 5px
27
+
28
+ .handle
29
+ position: absolute
30
+ cursor: move
31
+
32
+ .attribute
33
+ display: block
34
+ float: left
35
+ padding-right: 5px
36
+
37
+ &.title
38
+ padding-left: 20px
39
+
40
+ a
41
+ text-decoration: none !important
42
+
43
+ .title
44
+ color: black
45
+ font-size: 100%
46
+ font-weight: bold
47
+ text-decoration: none
48
+
49
+ &:hover
50
+ color: #0066cc
51
+ text-decoration: underline
52
+
53
+ .info
54
+ color: #9eb3bf
55
+ font-style: italic
56
+ font-weight: normal
57
+
58
+ &.virtual .page a .title
59
+ color: #9eb3bf
60
+
61
+ .w1
62
+ display: block
63
+
64
+ .expander
65
+ position: absolute
66
+ left: 0
67
+ top: 13px
68
+ padding: 0 3px
69
+
70
+ .add_child, .remove
71
+ position: absolute
72
+ top: 3px
73
+ font-size: 14px
74
+ width: 100px
75
+
76
+ .add_child
77
+ right: 100px
78
+ padding-left: 0
79
+ a
80
+ color: black
81
+ text-decoration: none
82
+ font-size: 80%
83
+ padding: 0 0 0 20px
84
+ background: url(/images/admin/plus.png) 4px 0 no-repeat
85
+ .remove
86
+ right: 0
87
+ padding-left: 0
88
+ a
89
+ color: black
90
+ text-decoration: none
91
+ font-size: 80%
92
+ padding: 0 0 0 20px
93
+ background: url(/images/admin/minus.png) 4px 0 no-repeat
94
+ .disabled
95
+ color: #ccc
96
+ font-size: 80%
97
+ padding: 0 0 0 20px
98
+ background: url(/images/admin/minus_disabled.png) 4px 0 no-repeat
99
+
100
+ &.children_visible
101
+ .children
102
+ li
103
+ display: block
104
+ &.children_hidden
105
+ .children
106
+ li
107
+ display: none
108
+
109
+ .children
110
+ border-top: 1px solid #FAFAFA
111
+
112
+ li
113
+ margin-left: 25px
114
+
10
115
 
11
- .drag_order
116
+ .handle
12
117
  width: 24px
13
118
 
14
119
  img
15
- visibility: hidden
16
120
  cursor: move
17
-
18
- &:hover
19
- img
20
- visibility: visible
21
-
22
-
23
- #drag_line
24
- position: relative
25
- height: 3px
26
- font-size: 0
27
- background-color: #800080
28
-
29
- div
30
- position: absolute
31
- height: 9px
32
- width: 9px
33
- margin: -3px 0 0 -9px
34
- background-image: url('/images/admin/extensions/drag_order/circle.png')
35
-
36
- &.copy
37
- height: 20px
38
- width: 20px
39
- margin: -9px 0 0 -20px
40
- background-image: url('/images/admin/extensions/drag_order/copy.png')
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{radiant-drag_order-extension}
8
- s.version = "0.3.9"
8
+ s.version = "0.4.0.beta.2"
9
9
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dirk Kelly"]
12
- s.date = %q{2010-11-24}
12
+ s.date = %q{2010-11-26}
13
13
  s.description = %q{Radiant DragOrder allows you to reorder pages funly}
14
14
  s.email = %q{dk@dirkkelly.com}
15
15
  s.extra_rdoc_files = [
@@ -19,9 +19,9 @@ Gem::Specification.new do |s|
19
19
  "README.md",
20
20
  "Rakefile",
21
21
  "VERSION",
22
- "app/views/admin/pages/_drag_order.html.haml",
23
- "app/views/admin/pages/_drag_order_header.html.haml",
24
- "app/views/admin/pages/_top.html.haml",
22
+ "app/views/admin/pages/_handle.html.haml",
23
+ "app/views/admin/pages/_node.html.haml",
24
+ "app/views/admin/pages/index.html.haml",
25
25
  "config/routes.rb",
26
26
  "db/migrate/01_add_position_to_pages.rb",
27
27
  "db/migrate/02_add_default_position.rb",
@@ -31,12 +31,17 @@ Gem::Specification.new do |s|
31
31
  "lib/drag_order/tags/core.rb",
32
32
  "lib/radiant-drag_order-extension.rb",
33
33
  "lib/tasks/drag_order_extension_tasks.rake",
34
+ "pkg/radiant-drag_order-extension-0.3.9.gem",
34
35
  "public/images/admin/extensions/drag_order/circle.png",
35
36
  "public/images/admin/extensions/drag_order/copy.png",
36
37
  "public/images/admin/extensions/drag_order/handle.png",
37
38
  "public/javascripts/admin/extensions/drag_order/drag_order.js",
39
+ "public/javascripts/admin/sitemap.js",
40
+ "public/javascripts/admin/sortable_tree.js",
38
41
  "public/stylesheets/sass/admin/extensions/drag_order/drag_order.sass",
39
42
  "radiant-drag_order-extension.gemspec",
43
+ "spec/controllers/pages_controller_spec.rb",
44
+ "spec/datasets/sort_pages.rb",
40
45
  "spec/spec.opts",
41
46
  "spec/spec_helper.rb"
42
47
  ]
@@ -45,6 +50,8 @@ Gem::Specification.new do |s|
45
50
  s.rubygems_version = %q{1.3.7}
46
51
  s.summary = %q{Drag Order Extension for Radiant CMS}
47
52
  s.test_files = [
53
+ "spec/controllers/pages_controller_spec.rb",
54
+ "spec/datasets/sort_pages.rb",
48
55
  "spec/spec_helper.rb"
49
56
  ]
50
57
 
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Admin::PagesController do
4
+
5
+ dataset :users, :sort_pages
6
+
7
+ before :each do
8
+ login_as :admin
9
+ end
10
+
11
+ describe '#sort' do
12
+ before :each do
13
+ @params = {
14
+ :parent_id => pages(:one).id,
15
+ :children => "#{pages(:four).id},#{pages(:three).id},#{pages(:two).id}"
16
+ }
17
+ end
18
+
19
+ context 'parent not sent' do
20
+ it 'should return an error' do
21
+ put :sort, :children => @params[:children], :format => 'js'
22
+
23
+ response.should_not be_success
24
+ response.body.should === 'Could not sort Pages.'
25
+ end
26
+ end
27
+
28
+ context 'children not sent' do
29
+ it 'should return an error' do
30
+ put :sort, :parent_id => @params[:parent_id], :format => 'js'
31
+
32
+ response.should_not be_success
33
+ response.body.should === 'Could not sort Pages.'
34
+ end
35
+ end
36
+
37
+ context 'parent and children sent' do
38
+ it 'should return success' do
39
+ put :sort, :parent_id => @params[:parent_id], :children => @params[:children], :format => 'js'
40
+
41
+ response.should be_success
42
+ response.body.should === 'Pages successfully sorted.'
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,31 @@
1
+ class SortPagesDataset < Dataset::Base
2
+ def load
3
+ # TODO Investigate why inflectors are breaking here
4
+ create_record :page, :one,
5
+ :title => 'One',
6
+ :slug => 'one',
7
+ :breadcrumb => 'one',
8
+ :position => 1
9
+
10
+ create_record :page, :two,
11
+ :title => 'Two',
12
+ :slug => 'two',
13
+ :breadcrumb => 'two',
14
+ :position => 2,
15
+ :parent => pages(:one)
16
+
17
+ create_record :page, :three,
18
+ :title => 'Three',
19
+ :slug => 'three',
20
+ :breadcrumb => 'three',
21
+ :position => 3,
22
+ :parent => pages(:one)
23
+
24
+ create_record :page, :four,
25
+ :title => 'Four',
26
+ :slug => 'four',
27
+ :breadcrumb => 'four',
28
+ :position => 4,
29
+ :parent => pages(:one)
30
+ end
31
+ end
data/spec/spec_helper.rb CHANGED
@@ -11,27 +11,12 @@ unless defined? RADIANT_ROOT
11
11
  end
12
12
  require "#{RADIANT_ROOT}/spec/spec_helper"
13
13
 
14
- if File.directory?(File.dirname(__FILE__) + "/scenarios")
15
- Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
16
- end
14
+ Dataset::Resolver.default << (File.dirname(__FILE__) + "/datasets")
15
+
17
16
  if File.directory?(File.dirname(__FILE__) + "/matchers")
18
17
  Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
19
18
  end
20
19
 
21
20
  Spec::Runner.configure do |config|
22
- # config.use_transactional_fixtures = true
23
- # config.use_instantiated_fixtures = false
24
- # config.fixture_path = RAILS_ROOT + '/spec/fixtures'
25
-
26
- # You can declare fixtures for each behaviour like this:
27
- # describe "...." do
28
- # fixtures :table_a, :table_b
29
- #
30
- # Alternatively, if you prefer to declare them only once, you can
31
- # do so here, like so ...
32
- #
33
- # config.global_fixtures = :table_a, :table_b
34
- #
35
- # If you declare global fixtures, be aware that they will be declared
36
- # for all of your examples, even those that don't use them.
21
+ config.mock_with :rr
37
22
  end
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radiant-drag_order-extension
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1
5
- prerelease: false
4
+ hash: 62196455
5
+ prerelease: true
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 9
10
- version: 0.3.9
8
+ - 4
9
+ - 0
10
+ - beta
11
+ - 2
12
+ version: 0.4.0.beta.2
11
13
  platform: ruby
12
14
  authors:
13
15
  - Dirk Kelly
@@ -15,7 +17,7 @@ autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2010-11-24 00:00:00 +08:00
20
+ date: 2010-11-26 00:00:00 +08:00
19
21
  default_executable:
20
22
  dependencies:
21
23
  - !ruby/object:Gem::Dependency
@@ -46,9 +48,9 @@ files:
46
48
  - README.md
47
49
  - Rakefile
48
50
  - VERSION
49
- - app/views/admin/pages/_drag_order.html.haml
50
- - app/views/admin/pages/_drag_order_header.html.haml
51
- - app/views/admin/pages/_top.html.haml
51
+ - app/views/admin/pages/_handle.html.haml
52
+ - app/views/admin/pages/_node.html.haml
53
+ - app/views/admin/pages/index.html.haml
52
54
  - config/routes.rb
53
55
  - db/migrate/01_add_position_to_pages.rb
54
56
  - db/migrate/02_add_default_position.rb
@@ -58,12 +60,17 @@ files:
58
60
  - lib/drag_order/tags/core.rb
59
61
  - lib/radiant-drag_order-extension.rb
60
62
  - lib/tasks/drag_order_extension_tasks.rake
63
+ - pkg/radiant-drag_order-extension-0.3.9.gem
61
64
  - public/images/admin/extensions/drag_order/circle.png
62
65
  - public/images/admin/extensions/drag_order/copy.png
63
66
  - public/images/admin/extensions/drag_order/handle.png
64
67
  - public/javascripts/admin/extensions/drag_order/drag_order.js
68
+ - public/javascripts/admin/sitemap.js
69
+ - public/javascripts/admin/sortable_tree.js
65
70
  - public/stylesheets/sass/admin/extensions/drag_order/drag_order.sass
66
71
  - radiant-drag_order-extension.gemspec
72
+ - spec/controllers/pages_controller_spec.rb
73
+ - spec/datasets/sort_pages.rb
67
74
  - spec/spec.opts
68
75
  - spec/spec_helper.rb
69
76
  has_rdoc: true
@@ -87,12 +94,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
87
94
  required_rubygems_version: !ruby/object:Gem::Requirement
88
95
  none: false
89
96
  requirements:
90
- - - ">="
97
+ - - ">"
91
98
  - !ruby/object:Gem::Version
92
- hash: 3
99
+ hash: 25
93
100
  segments:
94
- - 0
95
- version: "0"
101
+ - 1
102
+ - 3
103
+ - 1
104
+ version: 1.3.1
96
105
  requirements: []
97
106
 
98
107
  rubyforge_project:
@@ -101,4 +110,6 @@ signing_key:
101
110
  specification_version: 3
102
111
  summary: Drag Order Extension for Radiant CMS
103
112
  test_files:
113
+ - spec/controllers/pages_controller_spec.rb
114
+ - spec/datasets/sort_pages.rb
104
115
  - spec/spec_helper.rb
@@ -1,4 +0,0 @@
1
- - if current_user.send("admin?") || current_user.send("designer?")
2
- - unless simple
3
- %td.drag_order
4
- = level > 0 ? order_dragger : ""
@@ -1,2 +0,0 @@
1
- - if current_user.send("admin?") || current_user.send("designer?")
2
- %th.drag_order
@@ -1,3 +0,0 @@
1
- - if current_user.send("admin?") || current_user.send("designer?")
2
- - include_stylesheet 'admin/extensions/drag_order/drag_order'
3
- - include_javascript 'admin/extensions/drag_order/drag_order'