aeonscope-rest 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +25 -0
- data/README.rdoc +42 -30
- data/Rakefile +2 -2
- data/VERSION.yml +1 -1
- data/lib/class_methods.rb +17 -17
- data/lib/instance_methods.rb +261 -0
- data/lib/resource_helper.rb +133 -50
- data/lib/rest.rb +2 -2
- data/rails_generators/{ujs_setup → rest_setup}/USAGE +2 -2
- data/rails_generators/rest_setup/rest_setup_generator.rb +28 -0
- data/rails_generators/{ujs_setup → rest_setup}/templates/README +1 -1
- data/rails_generators/{ujs_setup → rest_setup}/templates/app/controllers/javascripts_controller.rb +0 -0
- data/rails_generators/{ujs_setup → rest_setup}/templates/app/views/javascripts/ujs.js.erb +3 -3
- data/rails_generators/rest_setup/templates/config/initializers/pagination.rb +10 -0
- data/rails_generators/rest_setup/templates/public/javascripts/jquery.rest.js +325 -0
- data/rails_generators/rest_setup/templates/public/themes/default/images/icons/rest/destroy.png +0 -0
- data/rails_generators/rest_setup/templates/public/themes/default/images/icons/rest/edit.png +0 -0
- data/rails_generators/rest_setup/templates/public/themes/default/images/icons/rest/new.png +0 -0
- data/rails_generators/rest_setup/templates/public/themes/default/images/icons/rest/show.png +0 -0
- metadata +17 -12
- data/lib/actions.rb +0 -230
- data/rails_generators/ujs_setup/templates/public/javascripts/rest.js +0 -119
- data/rails_generators/ujs_setup/ujs_setup_generator.rb +0 -18
data/lib/resource_helper.rb
CHANGED
@@ -1,63 +1,146 @@
|
|
1
1
|
module ResourceHelper
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# Renders a label for the current controller resource and action. Useful for labeling index, show, new, and edit views.
|
3
|
+
# Format: <action> <suffix || resource name>, Example: "New Post"
|
4
|
+
# *suffix* - Optional. The suffix to be applied to the action name. Defaults to the controller name (i.e. resource).
|
5
|
+
def render_action_label suffix = nil
|
6
|
+
[params[:action].capitalize, (suffix || controller_name.singularize.capitalize)] * ' '
|
7
|
+
end
|
8
|
+
|
9
|
+
# Answers whether the given action is equal to the current action.
|
10
|
+
def is_action? action
|
11
|
+
params[:action] == action.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
# Answers if the current action is an index action.
|
15
|
+
def index_action?
|
16
|
+
is_action? :index
|
17
|
+
end
|
18
|
+
|
19
|
+
# Answers if the current action is show action.
|
20
|
+
def show_action?
|
21
|
+
is_action? :show
|
22
|
+
end
|
23
|
+
|
24
|
+
# Answers if the current action is a new action.
|
25
|
+
def new_action?
|
26
|
+
is_action? :new
|
27
|
+
end
|
28
|
+
|
29
|
+
# Answers if the current action is a create action.
|
30
|
+
def create_action?
|
31
|
+
is_action? :create
|
32
|
+
end
|
33
|
+
|
34
|
+
# Answers if the current action is an edit action.
|
35
|
+
def edit_action?
|
36
|
+
is_action? :edit
|
37
|
+
end
|
38
|
+
|
39
|
+
# Answers if the current action is an update action.
|
40
|
+
def update_action?
|
41
|
+
is_action? :update
|
42
|
+
end
|
43
|
+
|
44
|
+
# Answers if the current action is a destroy action.
|
45
|
+
def destroy_action?
|
46
|
+
is_action? :destroy
|
47
|
+
end
|
48
|
+
|
49
|
+
# Toggles hiding a group of records if the given collection is empty. Use as a class attribute.
|
50
|
+
# * *collection* - The collection of records.
|
51
|
+
def toggle_group_class collection
|
52
|
+
"hidden" if collection.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Builds a DOM ID for a given record. Works the same as the Rails dom_id helper except that it returns a record ID with
|
56
|
+
# a "_0" suffix for new records instead of a "new_" prefix. This makes record IDs consistent and attaching JavaScript
|
57
|
+
# events easier.
|
4
58
|
def build_dom_id record
|
5
59
|
name = record.class.name.underscore
|
6
|
-
record.new_record? ?
|
60
|
+
id = record.new_record? ? 0 : record.id
|
61
|
+
[name, id].compact * '_'
|
7
62
|
end
|
8
63
|
|
9
|
-
#
|
10
|
-
# * *
|
11
|
-
def
|
12
|
-
[params[:action].capitalize,
|
64
|
+
# Renders a descriptive label based on the current controller action. Useful for new/edit actions.
|
65
|
+
# * *suffix* - The action label.
|
66
|
+
def render_render_action_label suffix
|
67
|
+
[params[:action].capitalize, suffix].compact * ' '
|
13
68
|
end
|
14
|
-
|
15
|
-
# Shows an unobtrusive jQuery link where the UJS event is attached to the link via the "destroy" class.
|
16
|
-
# If JavaScript is disabled then the _show_ action will be called instead of the _destroy_ action. Accepts
|
17
|
-
# the following hash arguments:
|
18
|
-
# * *parent_id* - The ID of the parent element for which the link is a child of. Example: post_1. *NOTE:* The parent ID takes precidence over the link ID if defined.
|
19
|
-
# * *id* - The link ID. Example: post_1_link. *NOTE:* The link ID _must_ include the parent ID.
|
20
|
-
# * *label* - The link label. Defaults to "Delete".
|
21
|
-
# * *url* - The destroy URL. Example: /posts/1. *NOTE:* The proper HTTP POST request will be handled by JavaScript.
|
22
|
-
def show_destroy_link options = {}
|
23
|
-
options.reverse_merge! :id => options[:parent_id].to_s + "_destroy"
|
24
|
-
options.reverse_merge! :label => "Delete", :class => "destroy", :url => '#' + options[:id].to_s
|
25
|
-
link_to options[:label], options[:url], :id => options[:id], :class => options[:class]
|
26
|
-
end
|
27
|
-
|
28
|
-
# Shows a nested, unobtrusive jQuery link where the UJS event is attached to the link via the "destroy-nested" class.
|
29
|
-
# If JavaScript is disabled then the _show_ action will be called instead of the _destroy_ action. Accepts
|
30
|
-
# the following hash arguments:
|
31
|
-
# * *object* - The model object to destroy.
|
32
|
-
# * *parent_id* - The ID of the parent element for which the link is a child of. Example: post_1. *NOTE:* The parent ID takes precidence over the link ID if defined.
|
33
|
-
# * *id* - The link ID. Example: post_1_link. *NOTE:* The link ID _must_ include the parent ID.
|
34
|
-
# * *label* - The link label. Defaults to "Delete".
|
35
|
-
# * *url* - The destroy URL. Example: /posts/1. *NOTE:* The proper HTTP POST request will be handled by JavaScript.
|
36
|
-
def show_destroy_nested_link options = {}
|
37
|
-
unless options[:object].new_record?
|
38
|
-
options.reverse_merge! :id => options[:parent_id].to_s + "_destroy-nested", :class => "destroy-nested"
|
39
|
-
show_destroy_link options
|
40
|
-
end
|
41
|
-
end
|
42
69
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
70
|
+
# Renders a label or an icon.
|
71
|
+
# * *label* - Optional (if use_icon=true). The link label. Defaults to "Unknown".
|
72
|
+
# * *use_icon* - Optional. Boolean for using a text or icon link. Defaults to false.
|
73
|
+
# * *icon_path* - Optional. The icon path. Defaults to "#{THEME_ROOT}/images/icons/unknown.png".
|
74
|
+
# * *icon_alt* - Optional. The icon text. Defaults to "Unknown Icon".
|
75
|
+
# * *icon_size* - Optional. The icon dimensions. Example: "{width}x{height}". Defaults to nil.
|
76
|
+
def render_label_or_icon options = {}
|
77
|
+
options.delete_if {|key, value| value.blank?}
|
78
|
+
options.reverse_merge! :label => "Uknown", :icon_path => "#{THEME_ROOT}/images/icons/unknown.png", :icon_alt => "Unknown Icon"
|
79
|
+
options[:use_icon] ? image_tag(options[:icon_path], :alt => options[:icon_alt], :size => options[:icon_size]) : options[:label]
|
48
80
|
end
|
49
81
|
|
50
|
-
#
|
51
|
-
|
52
|
-
# * *
|
53
|
-
|
54
|
-
|
82
|
+
# Renders an UJS show link.
|
83
|
+
# * *label* - Optional. The link label. Defaults to "Show".
|
84
|
+
# * *id* - Optional. The link ID. Defaults to nil.
|
85
|
+
# * *class* - Optional. The link class. Defaults to "show".
|
86
|
+
# * *url* - Required. The link URL (example: /posts/1). Defaults to "#show".
|
87
|
+
# * *use_icon* - Optional. Boolean for using a text or icon link. Defaults to false.
|
88
|
+
# * *icon_path* - Optional. The icon path. Defaults to "#{THEME_ROOT}/images/icons/rest/show.png".
|
89
|
+
# * *icon_alt* - Optional. The icon text. Defaults to "Show Icon".
|
90
|
+
# * *icon_size* - Optional. The icon dimensions. Example: "{width}x{height}". Defaults to nil.
|
91
|
+
def render_show_link options = {}
|
92
|
+
options.delete_if {|key, value| value.blank?}
|
93
|
+
options.reverse_merge! :label => "Show", :icon_path => "#{THEME_ROOT}/images/icons/rest/show.png", :icon_alt => "Show Icon", :class => "show", :url => "#show"
|
94
|
+
link_to render_label_or_icon(options), options[:url], :id => options[:id], :class => options[:class]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Renders an UJS new link.
|
98
|
+
# * *label* - Optional. The link label. Defaults to "New".
|
99
|
+
# * *id* - Optional. The link ID. Defaults to nil.
|
100
|
+
# * *class* - Optional. The link class. Defaults to "new".
|
101
|
+
# * *type* - Optional. Determines the type of object be created. Specifiy "input" as the type for form additions. Defaults to nil.
|
102
|
+
# * *ogid* = Optional (if type=nil). The outer group ID. Defaults to nil.
|
103
|
+
# * *igid* = Optional (if type=nil). The inner group ID. Defaults to nil.
|
104
|
+
# * *position* = Optional (if type=nil). A number position of the ID to be modified. Useful when dealing with complex nested forms. Defaults to 0.
|
105
|
+
# * *url* - Optional (if type="input"). The link URL (example: /posts/new). Defaults to "#new".
|
106
|
+
# * *use_icon* - Optional. Boolean for using a text or icon link. Defaults to false.
|
107
|
+
# * *icon_path* - Optional. The icon path. Defaults to "#{THEME_ROOT}/images/icons/rest/new.png".
|
108
|
+
# * *icon_alt* - Optional. The icon text. Defaults to "New Icon".
|
109
|
+
# * *icon_size* - Optional. The icon dimensions. Example: "{width}x{height}". Defaults to nil.
|
110
|
+
def render_new_link options = {}
|
111
|
+
options.delete_if {|key, value| value.blank?}
|
112
|
+
options.reverse_merge! :label => "New", :icon_path => "#{THEME_ROOT}/images/icons/rest/new.png", :icon_alt => "New Icon", :class => "new", :url => "#new", "data-position" => 0
|
113
|
+
link_to render_label_or_icon(options), options[:url], :id => options[:id], :class => options[:class], "data-type" => options[:type], "data-ogid" => options[:ogid], "data-igid" => options[:igid], "data-position" => options[:position]
|
55
114
|
end
|
56
115
|
|
57
|
-
#
|
58
|
-
|
59
|
-
# * *
|
60
|
-
|
61
|
-
|
116
|
+
# Renders an UJS edit link.
|
117
|
+
# * *label* - Optional. The link label. Defaults to "Edit".
|
118
|
+
# * *id* - Optional. The link ID. Defaults to nil.
|
119
|
+
# * *class* - Optional. The link class. Defaults to "edit".
|
120
|
+
# * *url* - Required. The link URL (example: /posts/1/edit). Defaults to "#edit".
|
121
|
+
# * *use_icon* - Optional. Boolean for using a text or icon link. Defaults to false.
|
122
|
+
# * *icon_path* - Optional. The icon path. Defaults to "#{THEME_ROOT}/images/icons/rest/edit.png".
|
123
|
+
# * *icon_alt* - Optional. The icon text. Defaults to "Edit Icon".
|
124
|
+
# * *icon_size* - Optional. The icon dimensions. Example: "{width}x{height}". Defaults to nil.
|
125
|
+
def render_edit_link options = {}
|
126
|
+
options.delete_if {|key, value| value.blank?}
|
127
|
+
options.reverse_merge! :label => "Edit", :icon_path => "#{THEME_ROOT}/images/icons/rest/edit.png", :icon_alt => "Edit Icon", :class => "edit", :url => "#edit"
|
128
|
+
link_to render_label_or_icon(options), options[:url], :id => options[:id], :class => options[:class]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Renders an UJS destroy link.
|
132
|
+
# * *label* - Optional. The link label. Defaults to "Delete".
|
133
|
+
# * *id* - Optional. The link ID. Defaults to nil.
|
134
|
+
# * *class* - Optional. The link class. Defaults to "destroy".
|
135
|
+
# * *type* - Optional. Determines the type of object be deleted. Specifiy "input" as the type for form deletions. Defaults to nil.
|
136
|
+
# * *url* - Required. The link URL (example: /posts/1). Defaults to "#destroy".
|
137
|
+
# * *use_icon* - Optional. Boolean for using a text or icon link. Defaults to false.
|
138
|
+
# * *icon_path* - Optional. The icon path. Defaults to "#{THEME_ROOT}/images/icons/rest/destroy.png".
|
139
|
+
# * *icon_alt* - Optional. The icon text. Defaults to "Delete Icon".
|
140
|
+
# * *icon_size* - Optional. The icon dimensions. Example: "{width}x{height}". Defaults to nil.
|
141
|
+
def render_destroy_link options = {}
|
142
|
+
options.delete_if {|key, value| value.blank?}
|
143
|
+
options.reverse_merge! :label => "Delete", :icon_path => "#{THEME_ROOT}/images/icons/rest/destroy.png", :icon_alt => "Delete Icon", :class => "destroy", :url => "#destroy"
|
144
|
+
link_to render_label_or_icon(options), options[:url], :id => options[:id], :class => options[:class], "data-type" => options[:type]
|
62
145
|
end
|
63
146
|
end
|
data/lib/rest.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "class_methods.rb")
|
2
|
-
require File.join(File.dirname(__FILE__), "
|
2
|
+
require File.join(File.dirname(__FILE__), "instance_methods.rb")
|
3
3
|
require File.join(File.dirname(__FILE__), "resource_helper.rb")
|
4
4
|
|
5
5
|
module Rest
|
@@ -8,5 +8,5 @@ module Rest
|
|
8
8
|
base.helper "resource"
|
9
9
|
base.helper_method :build_resource_url
|
10
10
|
end
|
11
|
-
include
|
11
|
+
include InstanceMethods
|
12
12
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Berserk Technologies
|
1
|
+
Berserk Technologies REST Setup Generator
|
2
2
|
|
3
3
|
Description:
|
4
4
|
Applies unobtrusive jQuery support to your Ruby on Rails application.
|
@@ -8,4 +8,4 @@ Requirements
|
|
8
8
|
2. jQuery UI 1.7 or higher.
|
9
9
|
|
10
10
|
Usage:
|
11
|
-
script/generate
|
11
|
+
script/generate rest_setup
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class RestSetupGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
# Configuration
|
5
|
+
m.file "config/initializers/pagination.rb", "config/initializers/pagination.rb"
|
6
|
+
|
7
|
+
# Controllers
|
8
|
+
m.file "app/controllers/javascripts_controller.rb", "app/controllers/javascripts_controller.rb"
|
9
|
+
|
10
|
+
# Views
|
11
|
+
m.directory "app/views/javascripts"
|
12
|
+
m.file "app/views/javascripts/ujs.js.erb", "app/views/javascripts/ujs.js.erb"
|
13
|
+
|
14
|
+
# JavaScript
|
15
|
+
m.file "public/javascripts/jquery.rest.js", "public/javascripts/jquery.rest.js"
|
16
|
+
|
17
|
+
# Views
|
18
|
+
m.directory "public/themes/default/images/icons/rest"
|
19
|
+
m.file "public/themes/default/images/icons/rest/show.png", "public/themes/default/images/icons/rest/show.png"
|
20
|
+
m.file "public/themes/default/images/icons/rest/new.png", "public/themes/default/images/icons/rest/new.png"
|
21
|
+
m.file "public/themes/default/images/icons/rest/edit.png", "public/themes/default/images/icons/rest/edit.png"
|
22
|
+
m.file "public/themes/default/images/icons/rest/destroy.png", "public/themes/default/images/icons/rest/destroy.png"
|
23
|
+
|
24
|
+
# Instructions
|
25
|
+
m.readme "README"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -4,8 +4,8 @@ Tasks You Need to Complete:
|
|
4
4
|
|
5
5
|
<%= javascript_include_tag "jquery" %>
|
6
6
|
<%= javascript_include_tag "jquery-ui" %>
|
7
|
+
<%= javascript_include_tag "jquery.rest" %>
|
7
8
|
<%= javascript_include_tag "ujs" %>
|
8
|
-
<%= javascript_include_tag "rest" %>
|
9
9
|
|
10
10
|
2. Add the following to the very bottom of your config/routes.rb file:
|
11
11
|
|
data/rails_generators/{ujs_setup → rest_setup}/templates/app/controllers/javascripts_controller.rb
RENAMED
File without changes
|
@@ -11,8 +11,8 @@ $.ajaxSetup({
|
|
11
11
|
*/
|
12
12
|
$(document).ajaxSend(function(event, request, settings){
|
13
13
|
var authToken = "<%= form_authenticity_token %>";
|
14
|
-
if (authToken != null){
|
15
|
-
settings.data = settings.data ||
|
16
|
-
settings.data += (settings.data ? "&" :
|
14
|
+
if (authToken != null) {
|
15
|
+
settings.data = settings.data || '';
|
16
|
+
settings.data += (settings.data ? "&" : '') + "authenticity_token=" + encodeURIComponent(authToken);
|
17
17
|
};
|
18
18
|
});
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Default options for the Will Paginate gem: http://github.com/mislav/will_paginate
|
2
|
+
require "will_paginate"
|
3
|
+
WillPaginate::ViewHelpers.pagination_options[:previous_label] = '<<'
|
4
|
+
WillPaginate::ViewHelpers.pagination_options[:next_label] = '>>'
|
5
|
+
WillPaginate::ViewHelpers.pagination_options[:inner_window] = 2
|
6
|
+
WillPaginate::ViewHelpers.pagination_options[:container] = false
|
7
|
+
|
8
|
+
# Default options for the REST gem: http://github.com/aeonscope/rest
|
9
|
+
PAGINATION_PARAM_NAME = :page
|
10
|
+
PAGINATION_PAGE_COUNT = 15
|
@@ -0,0 +1,325 @@
|
|
1
|
+
(function($) {
|
2
|
+
// Global plugin settings.
|
3
|
+
var settings = null;
|
4
|
+
|
5
|
+
/* Compares the first number found in a pair of strings in order to determine sort order.
|
6
|
+
* @a - The first number.
|
7
|
+
* @b - The second number.
|
8
|
+
*/
|
9
|
+
function compareFirstNumber(a, b) {
|
10
|
+
a = new Number(a.match(/\d+/));
|
11
|
+
b = new Number(b.match(/\d+/));
|
12
|
+
if (a < b) { return -1; }
|
13
|
+
if (a > b) { return 1; }
|
14
|
+
return 0;
|
15
|
+
}
|
16
|
+
|
17
|
+
/* Chops off the last string segment designated by delimiter.
|
18
|
+
* If no delimiter is found then the original string is returned instead.
|
19
|
+
* @string - Required. The string to chop.
|
20
|
+
* @delimiter - Optional. The delimiter used to chop up the string. Defaults to '_'.
|
21
|
+
*/
|
22
|
+
function stringChop(string, delimiter) {
|
23
|
+
var chopped = string;
|
24
|
+
if (delimiter == undefined) { delimiter = '_'; }
|
25
|
+
var endIndex = string.lastIndexOf(delimiter);
|
26
|
+
if (endIndex > 1) {chopped = string.slice(0, endIndex);}
|
27
|
+
return chopped;
|
28
|
+
}
|
29
|
+
|
30
|
+
/* Answers the ID found in the string based off of a certain delimiter.
|
31
|
+
* @string - Required. The string to obtain the ID from.
|
32
|
+
* @delimiter - Optional. The delimiter used to distinquish the ID from the string. Defaults to: '_'.
|
33
|
+
*/
|
34
|
+
function getId(string, delimiter) {
|
35
|
+
var id = string;
|
36
|
+
if (delimiter == undefined) { delimiter = '_'; }
|
37
|
+
var endIndex = string.lastIndexOf(delimiter) + 1;
|
38
|
+
if (endIndex < string.length) {id = string.slice(endIndex);}
|
39
|
+
return id;
|
40
|
+
}
|
41
|
+
|
42
|
+
/* Replaces an existing number within a string with a new number based on position in the string (either first or last postion).
|
43
|
+
* @string - The string for which to replace an old number with a new number.
|
44
|
+
* @newNumber - The new number to be replaced in the string.
|
45
|
+
* @position - The position of the old number in the string to be replaced.
|
46
|
+
*/
|
47
|
+
function replaceNumber(string, newNumber, position) {
|
48
|
+
if (string != undefined) {
|
49
|
+
if (position == undefined) { position = 0; }
|
50
|
+
var numbers = string.match(/\d+/g);
|
51
|
+
if (numbers != null && numbers.length > 1) {
|
52
|
+
var oldNumber;
|
53
|
+
var index;
|
54
|
+
switch(position) {
|
55
|
+
case 0:
|
56
|
+
oldNumber = numbers[0];
|
57
|
+
index = string.indexOf(oldNumber);
|
58
|
+
break;
|
59
|
+
case 1:
|
60
|
+
oldNumber = numbers.reverse()[0];
|
61
|
+
index = string.lastIndexOf(oldNumber);
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
var prefix = string.substring(0, index);
|
65
|
+
var suffix = string.substring(index + oldNumber.length, string.length);
|
66
|
+
string = prefix + newNumber + suffix;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
return string;
|
70
|
+
}
|
71
|
+
|
72
|
+
/* Increments the first or last number in a string (if any, defaults to "first").
|
73
|
+
* If no number is found then the original string is returned.
|
74
|
+
* @string - The string to manipulate.
|
75
|
+
* @position - The position of the number (first or last) to increment.
|
76
|
+
*/
|
77
|
+
function incrementNumber(string, position) {
|
78
|
+
if (string != undefined) {
|
79
|
+
if (position == undefined) { position = "first"; }
|
80
|
+
var numbers = string.match(/\d+/g);
|
81
|
+
if (numbers != null) {
|
82
|
+
var oldNumber;
|
83
|
+
var newNumber;
|
84
|
+
var index;
|
85
|
+
switch(position) {
|
86
|
+
case "first":
|
87
|
+
oldNumber = numbers[0];
|
88
|
+
newNumber = new Number(oldNumber) + 1;
|
89
|
+
index = string.indexOf(oldNumber);
|
90
|
+
break;
|
91
|
+
case "last":
|
92
|
+
oldNumber = numbers.reverse()[0];
|
93
|
+
newNumber = new Number(oldNumber) + 1;
|
94
|
+
index = string.lastIndexOf(oldNumber);
|
95
|
+
break;
|
96
|
+
}
|
97
|
+
var prefix = string.substring(0, index);
|
98
|
+
var suffix = string.substring(index + oldNumber.length, string.length);
|
99
|
+
string = prefix + newNumber + suffix;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return string;
|
103
|
+
}
|
104
|
+
|
105
|
+
/* Builds an array of all body element attributes (if not already set).
|
106
|
+
* @attribute - The attribute to find.
|
107
|
+
*/
|
108
|
+
function buildBodyAttributes(attribute) {
|
109
|
+
bodyAttributes = $.map($("body").find('[' + attribute + ']'), function(element) {
|
110
|
+
return $(element).attr(attribute);
|
111
|
+
});
|
112
|
+
return bodyAttributes;
|
113
|
+
}
|
114
|
+
|
115
|
+
/* Makes an array of attributes unique by comparing each attribute in the array against all body element attributes.
|
116
|
+
* @attributes - The attributes to make unique.
|
117
|
+
* @attribute - The attribute to search for.
|
118
|
+
* @index - The starting index in the arravy of attributes.
|
119
|
+
*/
|
120
|
+
function uniquifyAttributes(attributes, attribute, index) {
|
121
|
+
if (index < attributes.length) {
|
122
|
+
var numbers = attributes[index].match(/\d+/g);
|
123
|
+
var strings = attributes[index].split(/\d+/);
|
124
|
+
if (numbers != null && numbers.length == 1) {
|
125
|
+
var constant = attributes[index].split(/\d+/).join('-');
|
126
|
+
var familiars = [];
|
127
|
+
var bodyAttributes = buildBodyAttributes(attribute);
|
128
|
+
for (x in bodyAttributes) {
|
129
|
+
var current = bodyAttributes[x].split(/\d+/).join('-');
|
130
|
+
if (current == constant) {
|
131
|
+
familiars.push(bodyAttributes[x]);
|
132
|
+
}
|
133
|
+
}
|
134
|
+
var last = incrementNumber(familiars.sort(compareFirstNumber).pop());
|
135
|
+
attributes.splice(index, 1, last);
|
136
|
+
bodyAttributes.push(last);
|
137
|
+
}
|
138
|
+
index++;
|
139
|
+
uniquifyAttributes(attributes, attribute, index);
|
140
|
+
}
|
141
|
+
bodyAttributes = null;
|
142
|
+
return attributes;
|
143
|
+
}
|
144
|
+
|
145
|
+
/* Ensures all child element attibutes are unique for a given root element.
|
146
|
+
* @element - TODO...
|
147
|
+
* @attribute - TODO...
|
148
|
+
* @selector - TODO...
|
149
|
+
*/
|
150
|
+
function uniquifyChildren(element, attribute, selector) {
|
151
|
+
// Acquire the child elements.
|
152
|
+
var children = $(element).find(selector);
|
153
|
+
var attributes = [];
|
154
|
+
// Extract the attributes from the child elements.
|
155
|
+
children.each(function() {
|
156
|
+
attributes.push($(this).attr(attribute));
|
157
|
+
});
|
158
|
+
// Make the attributes unique.
|
159
|
+
attributes = uniquifyAttributes(attributes, attribute, 0);
|
160
|
+
// Update the child elements with the unique attributes.
|
161
|
+
for (x in attributes) {
|
162
|
+
$(children[x]).attr(attribute, attributes[x]);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
/* Generates a hidden input field based off original input field data that instructs ActiveRecord to delete
|
167
|
+
* a record based on the ID of the input field.
|
168
|
+
* @input - The input element from which to build the deletion input element.
|
169
|
+
*/
|
170
|
+
function generateDestroyInput(input) {
|
171
|
+
if (input == undefined || $(input).length) {
|
172
|
+
$(input).attr("id", stringChop($(input).attr("id")) + "__delete");
|
173
|
+
$(input).attr("name", stringChop($(input).attr("name"), '[') + "[_delete]");
|
174
|
+
$(input).val(1);
|
175
|
+
return input;
|
176
|
+
} else {
|
177
|
+
return null;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
/* Animates the deletion of a DOM element. Defaults to simply fading and hiding the element from view but
|
182
|
+
* can be forced to remove the element from the DOM completely.
|
183
|
+
* @element - The element to destroy.
|
184
|
+
* @remove - Boolean for whether to remove or just hide the element.
|
185
|
+
*/
|
186
|
+
function animateDestroy(element, remove) {
|
187
|
+
if (remove == undefined) { remove = false; }
|
188
|
+
$(element).fadeOut(500, function() {
|
189
|
+
if (remove) { $(this).remove(); }
|
190
|
+
});
|
191
|
+
}
|
192
|
+
|
193
|
+
/* Executes the new action.
|
194
|
+
* @element - The element that spawned the new action.
|
195
|
+
*/
|
196
|
+
function newInputAction(element) {
|
197
|
+
// Outer group ID
|
198
|
+
var ogid = '#' + $(element).attr("data-ogid");
|
199
|
+
// Inner group ID
|
200
|
+
var igid = '#' + $(element).attr("data-igid");
|
201
|
+
// Position
|
202
|
+
var position = $(element).attr("data-position");
|
203
|
+
|
204
|
+
if ($(ogid).hasClass("hidden")) {
|
205
|
+
$(ogid).removeAttr("style").fadeIn(500, function() {
|
206
|
+
$(this).removeClass("hidden");
|
207
|
+
});
|
208
|
+
// Delete hidden metadata (if any).
|
209
|
+
$(settings.recordSelector + ":visible input:hidden[id$=__delete]").remove();
|
210
|
+
} else {
|
211
|
+
var count = $(igid).children(settings.recordSelector + ":visible").size();
|
212
|
+
var record = $(igid).children(settings.recordSelector + ":visible:last").clone(true);
|
213
|
+
record.attr("id", uniquifyAttributes([record.attr("id")], "id", 0).toString());
|
214
|
+
$(igid).append(record);
|
215
|
+
// Remove excess cloned records.
|
216
|
+
var records = $.makeArray($(record).find(settings.recordSelector));
|
217
|
+
if (records.length > 1) {
|
218
|
+
records.shift();
|
219
|
+
for (x in records) {
|
220
|
+
$(records[x]).remove();
|
221
|
+
}
|
222
|
+
}
|
223
|
+
// Ensure the cloned children are unique.
|
224
|
+
uniquifyChildren(record, "id", "[id]:not([id*=attributes])");
|
225
|
+
uniquifyChildren(record, "for", "[for]");
|
226
|
+
uniquifyChildren(record, "data-ogid", "[data-ogid]");
|
227
|
+
uniquifyChildren(record, "data-igid", "[data-igid]");
|
228
|
+
|
229
|
+
// Increment the cloned, nested children.
|
230
|
+
$(record).find("[id*=attributes]").each(function() {
|
231
|
+
if (position == 0) {
|
232
|
+
$(this).attr("id", replaceNumber($(this).attr("id"), count));
|
233
|
+
}
|
234
|
+
$(this).attr("id", incrementNumber($(this).attr("id"), "last"));
|
235
|
+
return this;
|
236
|
+
});
|
237
|
+
$(record).find("[name*=attributes]").each(function() {
|
238
|
+
if (position == 0) {
|
239
|
+
$(this).attr("name", replaceNumber($(this).attr("name"), count));
|
240
|
+
}
|
241
|
+
$(this).attr("name", incrementNumber($(this).attr("name"), "last"));
|
242
|
+
return this;
|
243
|
+
});
|
244
|
+
|
245
|
+
// Clear inputs.
|
246
|
+
$(record).find("input:visible").val('');
|
247
|
+
$(record).find("select:visible").val('');
|
248
|
+
// Delete hidden metadata.
|
249
|
+
$(record).find("input:hidden[id$=_id]").remove();
|
250
|
+
}
|
251
|
+
|
252
|
+
return false;
|
253
|
+
}
|
254
|
+
|
255
|
+
// Executes the destroy action.
|
256
|
+
function destroyAction(element, message) {
|
257
|
+
var result = confirm(message);
|
258
|
+
if (result) {
|
259
|
+
$.post($(element).attr("href"), "_method=delete");
|
260
|
+
animateDestroy($(element).closest(settings.recordSelector + ":visible"));
|
261
|
+
}
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
|
265
|
+
// Executes the destroy input action.
|
266
|
+
function destroyInputAction(element) {
|
267
|
+
var group = $(element).closest(settings.groupSelector);
|
268
|
+
var record = $(element).closest(settings.recordSelector);
|
269
|
+
// Create hidden deletion input from original identifier input so Rails knows to delete the record.
|
270
|
+
// NOTE: The following three lines are a workaround to a Safari and IE bug where the hidden input
|
271
|
+
// is not found using: $(record).prev("input");
|
272
|
+
var idName = stringChop($(record).attr("id"));
|
273
|
+
var idNumber = getId($(record).attr("id"));
|
274
|
+
var hiddenInput = $(group).find("input:hidden").filter("[name$=[id]][value=" + idNumber + ']');
|
275
|
+
// $("#rule_1_table").find("input:hidden").filter("[name$=[id]][value=6]");
|
276
|
+
|
277
|
+
if (hiddenInput.length > 0) {
|
278
|
+
$(record).prepend(generateDestroyInput($(hiddenInput).clone()));
|
279
|
+
}
|
280
|
+
// Remove the record from view or hide entire group if only one record left.
|
281
|
+
if ($(group).find(".record:visible").size() > 1) {
|
282
|
+
if (hiddenInput.length > 0) {
|
283
|
+
animateDestroy(record);
|
284
|
+
} else {
|
285
|
+
animateDestroy(record, true);
|
286
|
+
}
|
287
|
+
} else {
|
288
|
+
$(record).find("input:visible").val('');
|
289
|
+
$(record).find("select:visible").val('');
|
290
|
+
$(group).fadeOut(500, function() {
|
291
|
+
$(group).addClass("hidden");
|
292
|
+
});
|
293
|
+
}
|
294
|
+
return false;
|
295
|
+
}
|
296
|
+
|
297
|
+
// Plugin
|
298
|
+
$.rest = function(options) {
|
299
|
+
settings = $.extend(true, {}, $.rest.defaults, options);
|
300
|
+
// New
|
301
|
+
$(settings.newSelector).live("click", function() {
|
302
|
+
return $(this).attr("data-type") == "input" ? newInputAction(this) : true;
|
303
|
+
});
|
304
|
+
// Edit
|
305
|
+
$(settings.editSelector).live("click", function() {
|
306
|
+
// TODO - Need to supply confirm/worning dialog here.
|
307
|
+
return true;
|
308
|
+
});
|
309
|
+
// Destroy
|
310
|
+
$(settings.destroySelector).live("click", function(event) {
|
311
|
+
return $(event.target).attr("data-type") == "input" ? destroyInputAction(this) : destroyAction(this, settings.destroyConfirm);
|
312
|
+
});
|
313
|
+
return false;
|
314
|
+
};
|
315
|
+
|
316
|
+
// Plugin Public Defaults
|
317
|
+
$.rest.defaults = {
|
318
|
+
newSelector: "a.new",
|
319
|
+
editSelector: "a.edit",
|
320
|
+
destroySelector: "a.destroy",
|
321
|
+
destroyConfirm: "Are you sure you want to delete this?",
|
322
|
+
groupSelector: ".group",
|
323
|
+
recordSelector: ".record"
|
324
|
+
};
|
325
|
+
}) (jQuery);
|