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

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.
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'