active_versioning_workflow 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +47 -1
- data/app/assets/javascripts/active_versioning/live-preview.js +70 -0
- data/app/assets/stylesheets/active_versioning/active-preview.scss +14 -0
- data/app/controllers/active_versioning/workflow/live_preview_controller.rb +9 -0
- data/app/views/active_versioning/workflow/live_preview/show.html.erb +63 -0
- data/config/routes.rb +3 -0
- data/lib/active_versioning/workflow.rb +15 -7
- data/lib/active_versioning/workflow/actions_with_preview.rb +15 -0
- data/lib/active_versioning/workflow/preview_link.rb +23 -0
- data/lib/active_versioning/workflow/show_resource.rb +4 -11
- data/lib/active_versioning/workflow/version.rb +1 -1
- data/lib/generators/active_versioning/templates/initializers/active_versioning_workflow.rb +1 -0
- data/vendor/assets/javascripts/active_versioning/form-serialize.js +267 -0
- data/vendor/assets/javascripts/active_versioning/lodash.custom.js +9 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faea94025b06a679c6073bc2eed8677ef498c5ed
|
4
|
+
data.tar.gz: df408b6ccfbda2d62d893908218abae42f3a9913
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70c31eea328365bd8991b110e32d01016e167d733da322b6302ab37b15332d19a417be4fe22630681696f85d932e3ab5c3562544a65233a5e44eeea4a4db7a64
|
7
|
+
data.tar.gz: 993dde8dae50274d1a4d703d28e2c5b88e0e12def10eb574eceeb2da5b985f3da834a287a7c62ff1bacbdd84d69161b5453fc07ddb6457b9ea1411d4dfe95e5d
|
data/README.md
CHANGED
@@ -142,7 +142,7 @@ end
|
|
142
142
|
When all is said and done, this will give us an awesome interface for version workflow in ActiveAdmin:
|
143
143
|
![example](https://s3.amazonaws.com/f.cl.ly/items/2i2h1T1J0v2h0C2n0v3N/Screen%20Shot%202015-09-23%20at%203.15.32%20PM.png)
|
144
144
|
|
145
|
-
###
|
145
|
+
### Preview
|
146
146
|
ActiveVersioning Workflow allows for some optional basic previewing functionality that leverages the non-admin controller for a resource. If you'd like to take advantage of previewing, here's how you'll want to set it up:
|
147
147
|
```ruby
|
148
148
|
class PostsController < ApplicationController
|
@@ -168,3 +168,49 @@ In the ActiveAdmin draft panel, you'll see an extra preview button:
|
|
168
168
|
![preview](https://s3.amazonaws.com/f.cl.ly/items/1a3E1I0N3G36211k3m3t/Screen%20Shot%202015-09-24%20at%209.15.47%20AM.png)
|
169
169
|
|
170
170
|
This will open a new window with our `PostsController#show` rendered out with the current draft version of our post.
|
171
|
+
|
172
|
+
You'll also be able to use `actions_with_preview` in place of `actions` inside the default `index` block in an ActiveAdmin resource definition:
|
173
|
+
```ruby
|
174
|
+
ActiveAdmin.register Post do
|
175
|
+
include ActiveVersioning::Workflow::Controller
|
176
|
+
|
177
|
+
permit_params :title, :body
|
178
|
+
|
179
|
+
index do
|
180
|
+
column :title
|
181
|
+
|
182
|
+
actions_with_preview
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
### Live Preview
|
188
|
+
|
189
|
+
If you're using the above preview functionality, you can also tap into some optional -- somewhat experimental -- real-time live preview on edit pages.
|
190
|
+
|
191
|
+
To use the real-time live preview, there's some additional setup required.
|
192
|
+
|
193
|
+
Mount ActiveVersioning Workflow's routes:
|
194
|
+
```
|
195
|
+
# config/routes.rb
|
196
|
+
|
197
|
+
mount ActiveVerioning::Workflow::Engine, as: 'active_versioning'
|
198
|
+
```
|
199
|
+
|
200
|
+
Inside a JS file included into the asset pipeline, invoke the previewer with:
|
201
|
+
```javascript
|
202
|
+
//= require active_versioning/live-preview
|
203
|
+
|
204
|
+
var updater = ActivePreview({
|
205
|
+
// URL to the page to preview
|
206
|
+
url: "http://example.com/preview",
|
207
|
+
|
208
|
+
// Where to find live-preview.html
|
209
|
+
frameSrc: "/active-versioning/live-preview.html"
|
210
|
+
});
|
211
|
+
|
212
|
+
// Use updater however you want to emit an update. For example, with Colonel Kurtz
|
213
|
+
var editor = new ColonelKurtz();
|
214
|
+
|
215
|
+
editor.listen(updater);
|
216
|
+
```
|
@@ -0,0 +1,70 @@
|
|
1
|
+
//= require active_versioning/form-serialize
|
2
|
+
//= require active_versioning/lodash.custom
|
3
|
+
|
4
|
+
(function (global) {
|
5
|
+
function send (form, callback) {
|
6
|
+
var http = new XMLHttpRequest();
|
7
|
+
var params = global.FormSerialize(form, { hash: true });
|
8
|
+
var method = (params['_method'] || form.method).toUpperCase();
|
9
|
+
|
10
|
+
http.open(method, form.action, true);;
|
11
|
+
|
12
|
+
// Send JSON because we already need to serialize form parameters into JSON
|
13
|
+
// to extract the method
|
14
|
+
http.setRequestHeader("Content-Type", "application/json");
|
15
|
+
|
16
|
+
// Accept JSON to save on paint time rendering the admin again
|
17
|
+
http.setRequestHeader("Accept", "application/json");
|
18
|
+
|
19
|
+
http.send(JSON.stringify(params));
|
20
|
+
|
21
|
+
http.addEventListener('readystatechange', function onUpdate (event) {
|
22
|
+
// We don't care about the payload, so trigger the callback as *soon*
|
23
|
+
// as we get the progress state, which indicates that content is loading.
|
24
|
+
if (http.readyState >= 2) {
|
25
|
+
http.removeEventListener('readystatechange', onUpdate);
|
26
|
+
callback();
|
27
|
+
}
|
28
|
+
})
|
29
|
+
|
30
|
+
return http;
|
31
|
+
}
|
32
|
+
|
33
|
+
function previewer (options) {
|
34
|
+
options = options || {};
|
35
|
+
|
36
|
+
var frame = document.createElement('iframe');
|
37
|
+
var form = document.querySelector('form');
|
38
|
+
var request = null;
|
39
|
+
|
40
|
+
frame.id = "live_preview";
|
41
|
+
frame.frameBorder = 0;
|
42
|
+
|
43
|
+
document.body.appendChild(frame);
|
44
|
+
document.body.className += ' am-live-preview';
|
45
|
+
|
46
|
+
var refresh = function () {
|
47
|
+
frame.contentWindow.postMessage(options.url, window.location.origin);
|
48
|
+
}
|
49
|
+
|
50
|
+
var update = global._.debounce(function() {
|
51
|
+
if (request) {
|
52
|
+
request.abort();
|
53
|
+
}
|
54
|
+
|
55
|
+
request = send(form, refresh);
|
56
|
+
}, 500, { leading: true, trailing: true, maxWait: 1500 });
|
57
|
+
|
58
|
+
frame.onload = function() {
|
59
|
+
frame.onload = null;
|
60
|
+
refresh();
|
61
|
+
form.addEventListener('change', update, true);
|
62
|
+
};
|
63
|
+
|
64
|
+
frame.src = options.frameSrc;
|
65
|
+
|
66
|
+
return update;
|
67
|
+
}
|
68
|
+
|
69
|
+
global.ActivePreview = previewer;
|
70
|
+
})(window);
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Live Preview</title>
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background: white;
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
10
|
+
margin: 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
iframe {
|
14
|
+
background: white;
|
15
|
+
bottom: 0;
|
16
|
+
height: 100%;
|
17
|
+
left: 0;
|
18
|
+
opacity: 0;
|
19
|
+
position: fixed;
|
20
|
+
top: 0;
|
21
|
+
transition: 0.2s opacity;
|
22
|
+
width: 100%;
|
23
|
+
}
|
24
|
+
|
25
|
+
.active {
|
26
|
+
opacity: 1;
|
27
|
+
z-index: 1
|
28
|
+
}
|
29
|
+
</style>
|
30
|
+
</head>
|
31
|
+
<body>
|
32
|
+
<iframe id="buffer" frameborder="0"></iframe>
|
33
|
+
|
34
|
+
<script>
|
35
|
+
var template = document.createElement('iframe');
|
36
|
+
template.frameBorder = 0;
|
37
|
+
|
38
|
+
var buffer = template.cloneNode();
|
39
|
+
|
40
|
+
window.addEventListener("message", function (event) {
|
41
|
+
var frame = template.cloneNode();
|
42
|
+
|
43
|
+
document.body.appendChild(frame);
|
44
|
+
|
45
|
+
frame.onload = function () {
|
46
|
+
frame.onload = null;
|
47
|
+
frame.className = 'active';
|
48
|
+
|
49
|
+
if (buffer.contentWindow) {
|
50
|
+
frame.contentWindow.scrollTo(0, buffer.contentWindow.scrollY);
|
51
|
+
}
|
52
|
+
|
53
|
+
setTimeout(function() {
|
54
|
+
buffer.remove();
|
55
|
+
buffer = frame;
|
56
|
+
}, 200);
|
57
|
+
}
|
58
|
+
|
59
|
+
frame.src = event.data;
|
60
|
+
}, false);
|
61
|
+
</script>
|
62
|
+
</body>
|
63
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -4,12 +4,20 @@ require 'generators/active_versioning/workflow_generator'
|
|
4
4
|
|
5
5
|
module ActiveVersioning
|
6
6
|
module Workflow
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
7
|
+
autoload :ActionsWithPreview, 'active_versioning/workflow/actions_with_preview'
|
8
|
+
autoload :Controller, 'active_versioning/workflow/controller'
|
9
|
+
autoload :DraftActions, 'active_versioning/workflow/draft_actions'
|
10
|
+
autoload :DSL, 'active_versioning/workflow/dsl'
|
11
|
+
autoload :Previewable, 'active_versioning/workflow/previewable'
|
12
|
+
autoload :PreviewLink, 'active_versioning/workflow/preview_link'
|
13
|
+
autoload :Router, 'active_versioning/workflow/router'
|
14
|
+
autoload :ShowResource, 'active_versioning/workflow/show_resource'
|
15
|
+
autoload :ShowVersion, 'active_versioning/workflow/show_version'
|
16
|
+
|
17
|
+
def self.previewable?(resource)
|
18
|
+
preview_controller = "#{resource.class.to_s.pluralize}Controller".safe_constantize
|
19
|
+
|
20
|
+
preview_controller.present? && preview_controller.singleton_class.ancestors.include?(Previewable)
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActiveVersioning
|
2
|
+
module Workflow
|
3
|
+
module ActionsWithPreview
|
4
|
+
include ActiveVersioning::Workflow::PreviewLink
|
5
|
+
|
6
|
+
def actions_with_preview
|
7
|
+
actions defaults: false do |resource|
|
8
|
+
item preview_link_for(resource, class: :member_link) if ActiveVersioning::Workflow.previewable?(resource)
|
9
|
+
|
10
|
+
defaults(resource, css_class: :member_link)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveVersioning
|
2
|
+
module Workflow
|
3
|
+
module PreviewLink
|
4
|
+
def preview_link_for(resource, link_options={})
|
5
|
+
link_to I18n.t('active_versioning.links.preview'), preview_path_for(resource), link_options.merge(
|
6
|
+
class: 'preview-link',
|
7
|
+
target: :blank
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def preview_path_for(resource, options = {})
|
12
|
+
resource_path = proc do |resource|
|
13
|
+
param = resource.try(:slug) || resource.to_param
|
14
|
+
route_key = resource.model_name.singular_route_key
|
15
|
+
|
16
|
+
options.fetch(:context, self).send("#{route_key}_path", param, _preview: true)
|
17
|
+
end
|
18
|
+
|
19
|
+
resource.try(:path, _preview: true) || resource_path.call(resource)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module ActiveVersioning
|
2
2
|
module Workflow
|
3
3
|
class ShowResource < ::ActiveAdmin::Views::Pages::Show
|
4
|
+
include ActiveVersioning::Workflow::PreviewLink
|
5
|
+
|
4
6
|
def main_content
|
5
7
|
instance_exec(resource, &show_block)
|
6
8
|
end
|
@@ -101,19 +103,10 @@ module ActiveVersioning
|
|
101
103
|
link_to I18n.t('active_versioning.links.edit'), [:edit, active_admin_namespace.name, resource], class: 'edit-link'
|
102
104
|
end
|
103
105
|
|
104
|
-
def preview_controller
|
105
|
-
@preview_controller ||= "#{resource_class.to_s.pluralize}Controller".safe_constantize
|
106
|
-
end
|
107
|
-
|
108
106
|
def preview_link
|
109
|
-
|
110
|
-
return ''
|
111
|
-
end
|
107
|
+
return '' unless ActiveVersioning::Workflow.previewable?(resource)
|
112
108
|
|
113
|
-
|
114
|
-
class: 'preview-link',
|
115
|
-
target: :blank
|
116
|
-
}
|
109
|
+
preview_link_for(resource)
|
117
110
|
end
|
118
111
|
|
119
112
|
def discard_link
|
@@ -2,3 +2,4 @@ require 'active_versioning/workflow'
|
|
2
2
|
|
3
3
|
ActiveAdmin::ResourceDSL.send(:include, ActiveVersioning::Workflow::DSL)
|
4
4
|
ActiveAdmin::Views::ActiveAdminForm.send(:include, ActiveVersioning::Workflow::DraftActions)
|
5
|
+
ActiveAdmin::Views::IndexAsTable::IndexTableFor.send(:include, ActiveVersioning::Workflow::ActionsWithPreview)
|
@@ -0,0 +1,267 @@
|
|
1
|
+
/**
|
2
|
+
* Form Serialize v0.7.1
|
3
|
+
* via https://github.com/defunctzombie/form-serialize
|
4
|
+
*/
|
5
|
+
|
6
|
+
(function(global) {
|
7
|
+
// get successful control from form and assemble into object
|
8
|
+
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
|
9
|
+
|
10
|
+
// types which indicate a submit action and are not successful controls
|
11
|
+
// these will be ignored
|
12
|
+
var k_r_submitter = /^(?:submit|button|image|reset|file)$/i;
|
13
|
+
|
14
|
+
// node names which could be successful controls
|
15
|
+
var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i;
|
16
|
+
|
17
|
+
// Matches bracket notation.
|
18
|
+
var brackets = /(\[[^\[\]]*\])/g;
|
19
|
+
|
20
|
+
// serializes form fields
|
21
|
+
// @param form MUST be an HTMLForm element
|
22
|
+
// @param options is an optional argument to configure the serialization. Default output
|
23
|
+
// with no options specified is a url encoded string
|
24
|
+
// - hash: [true | false] Configure the output type. If true, the output will
|
25
|
+
// be a js object.
|
26
|
+
// - serializer: [function] Optional serializer function to override the default one.
|
27
|
+
// The function takes 3 arguments (result, key, value) and should return new result
|
28
|
+
// hash and url encoded str serializers are provided with this module
|
29
|
+
// - disabled: [true | false]. If true serialize disabled fields.
|
30
|
+
// - empty: [true | false]. If true serialize empty fields
|
31
|
+
function serialize(form, options) {
|
32
|
+
if (typeof options != 'object') {
|
33
|
+
options = { hash: !!options };
|
34
|
+
}
|
35
|
+
else if (options.hash === undefined) {
|
36
|
+
options.hash = true;
|
37
|
+
}
|
38
|
+
|
39
|
+
var result = (options.hash) ? {} : '';
|
40
|
+
var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize);
|
41
|
+
|
42
|
+
var elements = form && form.elements ? form.elements : [];
|
43
|
+
|
44
|
+
//Object store each radio and set if it's empty or not
|
45
|
+
var radio_store = Object.create(null);
|
46
|
+
|
47
|
+
for (var i=0 ; i<elements.length ; ++i) {
|
48
|
+
var element = elements[i];
|
49
|
+
|
50
|
+
// ingore disabled fields
|
51
|
+
if ((!options.disabled && element.disabled) || !element.name) {
|
52
|
+
continue;
|
53
|
+
}
|
54
|
+
// ignore anyhting that is not considered a success field
|
55
|
+
if (!k_r_success_contrls.test(element.nodeName) ||
|
56
|
+
k_r_submitter.test(element.type)) {
|
57
|
+
continue;
|
58
|
+
}
|
59
|
+
|
60
|
+
var key = element.name;
|
61
|
+
var val = element.value;
|
62
|
+
|
63
|
+
// we can't just use element.value for checkboxes cause some browsers lie to us
|
64
|
+
// they say "on" for value when the box isn't checked
|
65
|
+
if ((element.type === 'checkbox' || element.type === 'radio') && !element.checked) {
|
66
|
+
val = undefined;
|
67
|
+
}
|
68
|
+
|
69
|
+
// If we want empty elements
|
70
|
+
if (options.empty) {
|
71
|
+
// for checkbox
|
72
|
+
if (element.type === 'checkbox' && !element.checked) {
|
73
|
+
val = '';
|
74
|
+
}
|
75
|
+
|
76
|
+
// for radio
|
77
|
+
if (element.type === 'radio') {
|
78
|
+
if (!radio_store[element.name] && !element.checked) {
|
79
|
+
radio_store[element.name] = false;
|
80
|
+
}
|
81
|
+
else if (element.checked) {
|
82
|
+
radio_store[element.name] = true;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
// if options empty is true, continue only if its radio
|
87
|
+
if (!val && element.type == 'radio') {
|
88
|
+
continue;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
else {
|
92
|
+
// value-less fields are ignored unless options.empty is true
|
93
|
+
if (!val) {
|
94
|
+
continue;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// multi select boxes
|
99
|
+
if (element.type === 'select-multiple') {
|
100
|
+
val = [];
|
101
|
+
|
102
|
+
var selectOptions = element.options;
|
103
|
+
var isSelectedOptions = false;
|
104
|
+
for (var j=0 ; j<selectOptions.length ; ++j) {
|
105
|
+
var option = selectOptions[j];
|
106
|
+
var allowedEmpty = options.empty && !option.value;
|
107
|
+
var hasValue = (option.value || allowedEmpty);
|
108
|
+
if (option.selected && hasValue) {
|
109
|
+
isSelectedOptions = true;
|
110
|
+
|
111
|
+
// If using a hash serializer be sure to add the
|
112
|
+
// correct notation for an array in the multi-select
|
113
|
+
// context. Here the name attribute on the select element
|
114
|
+
// might be missing the trailing bracket pair. Both names
|
115
|
+
// "foo" and "foo[]" should be arrays.
|
116
|
+
if (options.hash && key.slice(key.length - 2) !== '[]') {
|
117
|
+
result = serializer(result, key + '[]', option.value);
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
result = serializer(result, key, option.value);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
// Serialize if no selected options and options.empty is true
|
126
|
+
if (!isSelectedOptions && options.empty) {
|
127
|
+
result = serializer(result, key, '');
|
128
|
+
}
|
129
|
+
|
130
|
+
continue;
|
131
|
+
}
|
132
|
+
|
133
|
+
result = serializer(result, key, val);
|
134
|
+
}
|
135
|
+
|
136
|
+
// Check for all empty radio buttons and serialize them with key=""
|
137
|
+
if (options.empty) {
|
138
|
+
for (var key in radio_store) {
|
139
|
+
if (!radio_store[key]) {
|
140
|
+
result = serializer(result, key, '');
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
return result;
|
146
|
+
}
|
147
|
+
|
148
|
+
function parse_keys(string) {
|
149
|
+
var keys = [];
|
150
|
+
var prefix = /^([^\[\]]*)/;
|
151
|
+
var children = new RegExp(brackets);
|
152
|
+
var match = prefix.exec(string);
|
153
|
+
|
154
|
+
if (match[1]) {
|
155
|
+
keys.push(match[1]);
|
156
|
+
}
|
157
|
+
|
158
|
+
while ((match = children.exec(string)) !== null) {
|
159
|
+
keys.push(match[1]);
|
160
|
+
}
|
161
|
+
|
162
|
+
return keys;
|
163
|
+
}
|
164
|
+
|
165
|
+
function hash_assign(result, keys, value) {
|
166
|
+
if (keys.length === 0) {
|
167
|
+
result = value;
|
168
|
+
return result;
|
169
|
+
}
|
170
|
+
|
171
|
+
var key = keys.shift();
|
172
|
+
var between = key.match(/^\[(.+?)\]$/);
|
173
|
+
|
174
|
+
if (key === '[]') {
|
175
|
+
result = result || [];
|
176
|
+
|
177
|
+
if (Array.isArray(result)) {
|
178
|
+
result.push(hash_assign(null, keys, value));
|
179
|
+
}
|
180
|
+
else {
|
181
|
+
// This might be the result of bad name attributes like "[][foo]",
|
182
|
+
// in this case the original `result` object will already be
|
183
|
+
// assigned to an object literal. Rather than coerce the object to
|
184
|
+
// an array, or cause an exception the attribute "_values" is
|
185
|
+
// assigned as an array.
|
186
|
+
result._values = result._values || [];
|
187
|
+
result._values.push(hash_assign(null, keys, value));
|
188
|
+
}
|
189
|
+
|
190
|
+
return result;
|
191
|
+
}
|
192
|
+
|
193
|
+
// Key is an attribute name and can be assigned directly.
|
194
|
+
if (!between) {
|
195
|
+
result[key] = hash_assign(result[key], keys, value);
|
196
|
+
}
|
197
|
+
else {
|
198
|
+
var string = between[1];
|
199
|
+
// +var converts the variable into a number
|
200
|
+
// better than parseInt because it doesn't truncate away trailing
|
201
|
+
// letters and actually fails if whole thing is not a number
|
202
|
+
var index = +string;
|
203
|
+
|
204
|
+
// If the characters between the brackets is not a number it is an
|
205
|
+
// attribute name and can be assigned directly.
|
206
|
+
if (isNaN(index)) {
|
207
|
+
result = result || {};
|
208
|
+
result[string] = hash_assign(result[string], keys, value);
|
209
|
+
}
|
210
|
+
else {
|
211
|
+
result = result || [];
|
212
|
+
result[index] = hash_assign(result[index], keys, value);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
return result;
|
217
|
+
}
|
218
|
+
|
219
|
+
// Object/hash encoding serializer.
|
220
|
+
function hash_serializer(result, key, value) {
|
221
|
+
var matches = key.match(brackets);
|
222
|
+
|
223
|
+
// Has brackets? Use the recursive assignment function to walk the keys,
|
224
|
+
// construct any missing objects in the result tree and make the assignment
|
225
|
+
// at the end of the chain.
|
226
|
+
if (matches) {
|
227
|
+
var keys = parse_keys(key);
|
228
|
+
hash_assign(result, keys, value);
|
229
|
+
}
|
230
|
+
else {
|
231
|
+
// Non bracket notation can make assignments directly.
|
232
|
+
var existing = result[key];
|
233
|
+
|
234
|
+
// If the value has been assigned already (for instance when a radio and
|
235
|
+
// a checkbox have the same name attribute) convert the previous value
|
236
|
+
// into an array before pushing into it.
|
237
|
+
//
|
238
|
+
// NOTE: If this requirement were removed all hash creation and
|
239
|
+
// assignment could go through `hash_assign`.
|
240
|
+
if (existing) {
|
241
|
+
if (!Array.isArray(existing)) {
|
242
|
+
result[key] = [ existing ];
|
243
|
+
}
|
244
|
+
|
245
|
+
result[key].push(value);
|
246
|
+
}
|
247
|
+
else {
|
248
|
+
result[key] = value;
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
return result;
|
253
|
+
}
|
254
|
+
|
255
|
+
// urlform encoding serializer
|
256
|
+
function str_serialize(result, key, value) {
|
257
|
+
// encode newlines as \r\n cause the html spec says so
|
258
|
+
value = value.replace(/(\r)?\n/g, '\r\n');
|
259
|
+
value = encodeURIComponent(value);
|
260
|
+
|
261
|
+
// spaces should be '+' rather than '%20'.
|
262
|
+
value = value.replace(/%20/g, '+');
|
263
|
+
return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value;
|
264
|
+
}
|
265
|
+
|
266
|
+
global.FormSerialize = serialize;
|
267
|
+
}(window))
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/**
|
2
|
+
* @license
|
3
|
+
* lodash (Custom Build) /license | Underscore.js 1.8.3 underscorejs.org/LICENSE
|
4
|
+
* Build: `lodash include="debounce"`
|
5
|
+
*/
|
6
|
+
;(function(){function t(){}function e(t){var e=typeof t;return!!t&&("object"==e||"function"==e)}function o(t){return!!t&&typeof t=="object"}function n(t){return typeof t=="symbol"||o(t)&&"[object Symbol]"==d.call(t)}function i(t){if(typeof t=="number")return t;if(n(t))return r;if(e(t)&&(t=typeof t.valueOf=="function"?t.valueOf():t,t=e(t)?t+"":t),typeof t!="string")return 0===t?t:+t;t=t.replace(u,"");var o=c.test(t);return o||a.test(t)?l(t.slice(2),o?2:8):f.test(t)?r:+t}var r=NaN,u=/^\s+|\s+$/g,f=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,a=/^0o[0-7]+$/i,l=parseInt,s=typeof self=="object"&&self&&self.Object===Object&&self,p=typeof global=="object"&&global&&global.Object===Object&&global||s||Function("return this")(),b=(s=typeof exports=="object"&&exports&&!exports.nodeType&&exports)&&typeof module=="object"&&module&&!module.nodeType&&module,d=Object.prototype.toString,y=Math.max,m=Math.min,v=function(){
|
7
|
+
return p.Date.now()};t.debounce=function(t,o,n){function r(e){var o=l,n=s;return l=s=void 0,g=e,b=t.apply(n,o)}function u(t){var e=t-j;return t-=g,void 0===j||e>=o||0>e||O&&t>=p}function f(){var t=v();if(u(t))return c(t);var e,n=setTimeout;e=t-g,t=o-(t-j),e=O?m(t,p-e):t,d=n(f,e)}function c(t){return d=void 0,T&&l?r(t):(l=s=void 0,b)}function a(){var t=v(),e=u(t);if(l=arguments,s=this,j=t,e){if(void 0===d)return g=t=j,d=setTimeout(f,o),x?r(t):b;if(O)return d=setTimeout(f,o),r(j)}return void 0===d&&(d=setTimeout(f,o)),
|
8
|
+
b}var l,s,p,b,d,j,g=0,x=false,O=false,T=true;if(typeof t!="function")throw new TypeError("Expected a function");return o=i(o)||0,e(n)&&(x=!!n.leading,p=(O="maxWait"in n)?y(i(n.maxWait)||0,o):p,T="trailing"in n?!!n.trailing:T),a.cancel=function(){void 0!==d&&clearTimeout(d),g=0,l=j=s=d=void 0},a.flush=function(){return void 0===d?b:c(v())},a},t.isObject=e,t.isObjectLike=o,t.isSymbol=n,t.now=v,t.toNumber=i,t.VERSION="4.15.0",typeof define=="function"&&typeof define.amd=="object"&&define.amd?(p._=t, define(function(){
|
9
|
+
return t})):b?((b.exports=t)._=t,s._=t):p._=t}).call(this);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_versioning_workflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Stenberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_versioning
|
@@ -108,16 +108,23 @@ files:
|
|
108
108
|
- README.md
|
109
109
|
- Rakefile
|
110
110
|
- active_versioning_workflow.gemspec
|
111
|
+
- app/assets/javascripts/active_versioning/live-preview.js
|
111
112
|
- app/assets/javascripts/active_versioning/workflow.js
|
113
|
+
- app/assets/stylesheets/active_versioning/active-preview.scss
|
112
114
|
- app/assets/stylesheets/active_versioning/workflow.scss
|
115
|
+
- app/controllers/active_versioning/workflow/live_preview_controller.rb
|
116
|
+
- app/views/active_versioning/workflow/live_preview/show.html.erb
|
113
117
|
- bin/console
|
114
118
|
- bin/setup
|
115
119
|
- circle.yml
|
120
|
+
- config/routes.rb
|
116
121
|
- lib/active_versioning/workflow.rb
|
122
|
+
- lib/active_versioning/workflow/actions_with_preview.rb
|
117
123
|
- lib/active_versioning/workflow/controller.rb
|
118
124
|
- lib/active_versioning/workflow/draft_actions.rb
|
119
125
|
- lib/active_versioning/workflow/dsl.rb
|
120
126
|
- lib/active_versioning/workflow/engine.rb
|
127
|
+
- lib/active_versioning/workflow/preview_link.rb
|
121
128
|
- lib/active_versioning/workflow/previewable.rb
|
122
129
|
- lib/active_versioning/workflow/router.rb
|
123
130
|
- lib/active_versioning/workflow/show_resource.rb
|
@@ -129,6 +136,8 @@ files:
|
|
129
136
|
- lib/generators/active_versioning/templates/initializers/active_versioning_workflow.rb
|
130
137
|
- lib/generators/active_versioning/templates/locales/active_versioning.en.yml
|
131
138
|
- lib/generators/active_versioning/workflow_generator.rb
|
139
|
+
- vendor/assets/javascripts/active_versioning/form-serialize.js
|
140
|
+
- vendor/assets/javascripts/active_versioning/lodash.custom.js
|
132
141
|
homepage: https://github.com/vigetlabs/active_versioning_workflow
|
133
142
|
licenses:
|
134
143
|
- BSD
|