aeonscope-rest 1.0.0 → 1.1.0
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/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);
|