joosy 1.2.0.alpha.14 → 1.2.0.alpha.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/Gruntfile.coffee +42 -18
- data/README.md +16 -12
- data/bower.json +1 -1
- data/lib/extensions/form.js +592 -0
- data/lib/extensions/preloaders.js +193 -0
- data/lib/extensions/resources.js +667 -0
- data/lib/joosy.js +1141 -2680
- data/lib/joosy.rb +0 -5
- data/package.json +1 -1
- data/spec/helpers/helper.coffee +1 -1
- data/spec/joosy/core/joosy_spec.coffee +1 -28
- data/spec/joosy/core/modules/renderer_spec.coffee +0 -95
- data/spec/joosy/{core → extensions/form}/form_spec.coffee +2 -2
- data/spec/joosy/{core → extensions/form}/helpers/forms_spec.coffee +1 -1
- data/spec/joosy/{preloaders → extensions/preloaders}/caching_spec.coffee +0 -0
- data/spec/joosy/{preloaders → extensions/preloaders}/inline_spec.coffee +0 -0
- data/spec/joosy/{core/resource/generic_spec.coffee → extensions/resources/base_spec.coffee} +19 -20
- data/spec/joosy/{core/resource → extensions/resources}/collection_spec.coffee +4 -4
- data/spec/joosy/{core/resource → extensions/resources}/rest_collection_spec.coffee +4 -4
- data/spec/joosy/{core/resource → extensions/resources}/rest_spec.coffee +6 -7
- data/src/joosy.coffee +1 -2
- data/src/joosy/core/helpers/view.coffee +0 -8
- data/src/joosy/core/joosy.coffee +5 -44
- data/src/joosy/core/modules/renderer.coffee +10 -29
- data/src/joosy/core/{resource → resources}/watcher.coffee +2 -1
- data/src/joosy/{core → extensions/form}/form.coffee +3 -9
- data/src/joosy/{core → extensions/form}/helpers/form.coffee +2 -2
- data/src/joosy/extensions/form/index.coffee +1 -0
- data/src/joosy/{preloaders → extensions/preloaders}/caching.coffee +0 -0
- data/src/joosy/extensions/preloaders/index.coffee +1 -0
- data/src/joosy/{preloaders → extensions/preloaders}/inline.coffee +0 -0
- data/src/joosy/{core/resource/generic.coffee → extensions/resources/base.coffee} +21 -21
- data/src/joosy/{core/resource → extensions/resources}/collection.coffee +11 -11
- data/src/joosy/extensions/resources/index.coffee +1 -0
- data/src/joosy/{core/resource → extensions/resources}/rest.coffee +15 -12
- data/src/joosy/{core/resource → extensions/resources}/rest_collection.coffee +4 -2
- data/src/joosy/generators/base.coffee +1 -1
- metadata +25 -20
- data/src/joosy/core/preloader.coffee +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5dd10943aa7e77f3b2b8819b0fe66a8849bf6f6
|
4
|
+
data.tar.gz: e97611fc855f28795886e99af976aaabc3a048ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 054a7481dab1f1f7f354d7390eb18fcd0d962531f361cc5b5844dcf8e18315794b2f101b32695e3526c973c50b4e615ccc039e6916c99292a0e8f03becc30b17
|
7
|
+
data.tar.gz: 3dd8453a8d90b61f489a4b5a699aed6d42d61fd593fa626951dc9b72c2562c1557603e0f7fd1933b183181b5ee8e503555111e871dda2cc9e25de45dfdc5c293
|
data/.gitignore
CHANGED
data/Gruntfile.coffee
CHANGED
@@ -12,11 +12,29 @@ module.exports = (grunt) ->
|
|
12
12
|
root: 'joosy.coffee'
|
13
13
|
path: 'src'
|
14
14
|
build: 'lib/joosy.js'
|
15
|
+
extensions: (name) ->
|
16
|
+
root: "joosy/extensions/#{name}"
|
17
|
+
build: "lib/extensions/#{name}.js"
|
15
18
|
specs:
|
16
|
-
units:
|
19
|
+
units:
|
20
|
+
core: 'spec/joosy/core/**/*_spec.*'
|
21
|
+
extensions: 'spec/joosy/extensions/**/*_spec.*'
|
17
22
|
helpers: 'spec/helpers/**/*.*'
|
18
23
|
build: '.grunt'
|
19
24
|
|
25
|
+
specOptions = (category, specs) ->
|
26
|
+
host: 'http://localhost:8888/'
|
27
|
+
keepRunner: true
|
28
|
+
outfile: "#{category}.html"
|
29
|
+
vendor: [
|
30
|
+
'components/sinonjs/sinon.js',
|
31
|
+
'components/jquery/jquery.js',
|
32
|
+
'components/jquery-form/jquery.form.js',
|
33
|
+
'components/sugar/release/sugar-full.min.js'
|
34
|
+
],
|
35
|
+
specs: "#{locations.specs.build}/#{specs}"
|
36
|
+
helpers: locations.specs.build + '/' + locations.specs.helpers
|
37
|
+
|
20
38
|
#
|
21
39
|
# Grunt extensions
|
22
40
|
#
|
@@ -48,21 +66,33 @@ module.exports = (grunt) ->
|
|
48
66
|
specs:
|
49
67
|
options:
|
50
68
|
nospawn: true
|
51
|
-
files: [locations.specs.units, locations.specs.helpers]
|
69
|
+
files: [locations.specs.units.core, locations.specs.units.extensions, locations.specs.helpers]
|
52
70
|
tasks: ['coffee', 'jasmine']
|
53
71
|
|
54
72
|
coffee:
|
55
73
|
specs:
|
56
74
|
expand: true
|
57
|
-
src: [locations.specs.units, locations.specs.helpers]
|
75
|
+
src: [locations.specs.units.core, locations.specs.units.extensions, locations.specs.helpers]
|
58
76
|
dest: locations.specs.build
|
59
77
|
ext: '.js'
|
60
78
|
|
61
79
|
mince:
|
62
|
-
|
80
|
+
core:
|
63
81
|
include: [locations.source.path]
|
64
82
|
src: locations.source.root
|
65
83
|
dest: locations.source.build
|
84
|
+
preloaders:
|
85
|
+
include: [locations.source.path]
|
86
|
+
src: locations.source.extensions('preloaders').root
|
87
|
+
dest: locations.source.extensions('preloaders').build
|
88
|
+
resources:
|
89
|
+
include: [locations.source.path]
|
90
|
+
src: locations.source.extensions('resources').root
|
91
|
+
dest: locations.source.extensions('resources').build
|
92
|
+
form:
|
93
|
+
include: [locations.source.path]
|
94
|
+
src: locations.source.extensions('form').root
|
95
|
+
dest: locations.source.extensions('form').build
|
66
96
|
|
67
97
|
coffeelint:
|
68
98
|
source:
|
@@ -73,20 +103,14 @@ module.exports = (grunt) ->
|
|
73
103
|
level: 'ignore'
|
74
104
|
|
75
105
|
jasmine:
|
76
|
-
|
106
|
+
core:
|
107
|
+
options: specOptions('core', locations.specs.units.core)
|
77
108
|
src: locations.source.build
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
'components/sinonjs/sinon.js',
|
84
|
-
'components/jquery/jquery.js',
|
85
|
-
'components/jquery-form/jquery.form.js',
|
86
|
-
'components/sugar/release/sugar-full.min.js'
|
87
|
-
],
|
88
|
-
specs: locations.specs.build + '/' + locations.specs.units
|
89
|
-
helpers: locations.specs.build + '/' + locations.specs.helpers
|
109
|
+
|
110
|
+
extensions:
|
111
|
+
options: specOptions('extensions', locations.specs.units.extensions)
|
112
|
+
src: [locations.source.build].include ['preloaders', 'resources', 'form'].map (x) ->
|
113
|
+
locations.source.extensions(x).build
|
90
114
|
|
91
115
|
grunt.event.on 'watch', (action, filepath) ->
|
92
116
|
grunt.config ['coffee', 'specs', 'src'], filepath
|
@@ -96,7 +120,7 @@ module.exports = (grunt) ->
|
|
96
120
|
#
|
97
121
|
grunt.registerTask 'default', ['connect', 'build', 'watch']
|
98
122
|
|
99
|
-
grunt.registerTask 'build', ['mince', 'coffee', 'jasmine:
|
123
|
+
grunt.registerTask 'build', ['mince', 'coffee', 'jasmine:core:build', 'jasmine:extensions:build', 'bowerize']
|
100
124
|
|
101
125
|
grunt.registerTask 'test', ['connect', 'mince', 'coffee', 'bowerize', 'jasmine']
|
102
126
|
|
data/README.md
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# Joosy 1.2: Feather
|
2
2
|
|
3
|
+
![Joosy](http://f.cl.ly/items/2N2J453J2B353F1A0t0I/joocy1.1.png)
|
4
|
+
|
5
|
+
Joosy is a javascript framework. Being a harmonious extensions to Rails it introduces everything you like about backend right to your browser. Ready conventions set, extensive CoffeeScript, HAML support, Helpers, seamless backend integration, automatic code generation and more.
|
6
|
+
|
3
7
|
[![NPM version](https://badge.fury.io/js/joosy.png)](http://badge.fury.io/js/joosy)
|
4
8
|
[![Build Status](https://travis-ci.org/joosy/joosy.png)](https://travis-ci.org/joosy/joosy)
|
5
9
|
[![Dependency Status](https://gemnasium.com/joosy/joosy.png)](https://gemnasium.com/joosy/joosy)
|
6
10
|
|
11
|
+
---
|
12
|
+
|
7
13
|
## WARNING!!!
|
8
14
|
|
9
15
|
Master branch is currently totally incompatible with the stable 1.1 and 1.0 branches. 1.2 features
|
@@ -31,11 +37,9 @@ to change or work not as expected while 1.2 branch is still early alpha.
|
|
31
37
|
|
32
38
|
Keep track on what's going on at the [Wiki](https://github.com/joosy/joosy/wiki#12-feather)
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
![Joosy](http://f.cl.ly/items/2N2J453J2B353F1A0t0I/joocy1.1.png)
|
40
|
+
---
|
37
41
|
|
38
|
-
|
42
|
+
## What is Joosy
|
39
43
|
|
40
44
|
Joosy allows you to create web apps which work completely in the browser. So that, it helps you to relocate all your Rails Views to the client side. It also helps you with managing the growing quantity of JS code. On another hand, it makes your backend to have exactly one function – to be a simple REST provider. That leads to easier development support and improves the scalability greatly.
|
41
45
|
|
@@ -51,11 +55,11 @@ Add Joosy gem to your Gemfile:
|
|
51
55
|
gem 'joosy-rails', '~> 1.0.0'
|
52
56
|
```
|
53
57
|
|
54
|
-
|
58
|
+
Use built-in generator to seed a basic application:
|
55
59
|
|
56
|
-
rails g joosy:application
|
60
|
+
rails g joosy:application
|
57
61
|
|
58
|
-
|
62
|
+
Make sure to remove `public/index.html` and you are ready to go with [localhost:3000](http://localhost:3000/). The main application code can be found at `app/assets/javascripts` directory. HTML canvas of the application is at `app/views/layouts/joosy.html.erb`.
|
59
63
|
|
60
64
|
### Jump in with Sinatra
|
61
65
|
|
@@ -69,24 +73,24 @@ Now run basic application generator with the following command:
|
|
69
73
|
|
70
74
|
joosy new dummy
|
71
75
|
|
72
|
-
The main application code will appear at `source` directory. `stylesheets` is for Stylus-based styles and the main canvas of page is defined at `source/index.haml`. Now you can `grunt joosy:server` to start development server at [localhost:4000
|
76
|
+
The main application code will appear at `source` directory. `stylesheets` is for Stylus-based styles and the main canvas of page is defined at `source/index.haml`. Now you can `grunt joosy:server` to start development server at [localhost:4000](http://localhost:4000/).
|
73
77
|
|
74
|
-
To generate assets statically prior to the deployment run
|
78
|
+
To generate assets statically prior to the deployment run:
|
75
79
|
|
76
80
|
grunt joosy:compile
|
77
81
|
|
78
|
-
|
82
|
+
You assets are at `public/` directory, enjoy!
|
79
83
|
|
80
84
|
## Hacking
|
81
85
|
|
82
86
|
* Ensure you have Node.js available on your system
|
83
87
|
* Clone the project
|
84
88
|
* Run `npm install` to get required Node modules
|
85
|
-
* Run `bower install` to get
|
89
|
+
* Run `bower install` to get required JS components
|
86
90
|
* Run `grunt test` to run specs once
|
87
91
|
* Run `grunt` to watch sources (automatic changes compilations) and run test-server (get your browser to http://localhost:8888/)
|
88
92
|
|
89
|
-
While current repository is
|
93
|
+
While current repository is at the same time NPM package, Ruby gem and Bower component, – the main Core
|
90
94
|
environment is Node.js.
|
91
95
|
|
92
96
|
## Credits
|
data/bower.json
CHANGED
@@ -0,0 +1,592 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
/*** src/joosy/extensions/form/form ***/
|
4
|
+
|
5
|
+
var __hasProp = {}.hasOwnProperty,
|
6
|
+
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
7
|
+
|
8
|
+
Joosy.Form = (function(_super) {
|
9
|
+
__extends(Form, _super);
|
10
|
+
|
11
|
+
Form.include(Joosy.Modules.Container);
|
12
|
+
|
13
|
+
Form.include(Joosy.Modules.Log);
|
14
|
+
|
15
|
+
Form.include(Joosy.Modules.Events);
|
16
|
+
|
17
|
+
Form.prototype.invalidationClass = 'field_with_errors';
|
18
|
+
|
19
|
+
Form.prototype.substitutions = {};
|
20
|
+
|
21
|
+
Form.prototype.elements = {
|
22
|
+
'fields': 'input,select,textarea'
|
23
|
+
};
|
24
|
+
|
25
|
+
Form.submit = function(form, options) {
|
26
|
+
if (options == null) {
|
27
|
+
options = {};
|
28
|
+
}
|
29
|
+
form = new this(form, options);
|
30
|
+
form.container.submit();
|
31
|
+
form.unbind();
|
32
|
+
return null;
|
33
|
+
};
|
34
|
+
|
35
|
+
Form.attach = function() {
|
36
|
+
return (function(func, args, ctor) {
|
37
|
+
ctor.prototype = func.prototype;
|
38
|
+
var child = new ctor, result = func.apply(child, args);
|
39
|
+
return Object(result) === result ? result : child;
|
40
|
+
})(Joosy.Form, arguments, function(){});
|
41
|
+
};
|
42
|
+
|
43
|
+
function Form(form, options) {
|
44
|
+
var method, _ref,
|
45
|
+
_this = this;
|
46
|
+
if (options == null) {
|
47
|
+
options = {};
|
48
|
+
}
|
49
|
+
if (Object.isFunction(options)) {
|
50
|
+
this.success = options;
|
51
|
+
} else {
|
52
|
+
Object.each(options, function(key, value) {
|
53
|
+
return _this[key] = value;
|
54
|
+
});
|
55
|
+
}
|
56
|
+
this.container = $(form);
|
57
|
+
if (this.container.length === 0) {
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
this.refreshElements();
|
61
|
+
this.__delegateEvents();
|
62
|
+
method = (_ref = this.container.get(0).getAttribute('method')) != null ? _ref.toLowerCase() : void 0;
|
63
|
+
if (method && !['get', 'post'].any(method)) {
|
64
|
+
this.__markMethod(method);
|
65
|
+
this.container.attr('method', 'POST');
|
66
|
+
}
|
67
|
+
this.container.ajaxForm({
|
68
|
+
dataType: 'json',
|
69
|
+
beforeSend: function() {
|
70
|
+
if (_this.__debounce.apply(_this, arguments)) {
|
71
|
+
return false;
|
72
|
+
}
|
73
|
+
_this.__before.apply(_this, arguments);
|
74
|
+
_this.__pending_request = true;
|
75
|
+
_this.debugAs(_this, 'beforeSend: pending_request = true');
|
76
|
+
return true;
|
77
|
+
},
|
78
|
+
success: function() {
|
79
|
+
_this.__pending_request = false;
|
80
|
+
_this.debugAs(_this, 'success: pending_request = false');
|
81
|
+
return _this.__success.apply(_this, arguments);
|
82
|
+
},
|
83
|
+
error: function() {
|
84
|
+
_this.__pending_request = false;
|
85
|
+
_this.debugAs(_this, 'error: pending_request = false');
|
86
|
+
return _this.__error.apply(_this, arguments);
|
87
|
+
},
|
88
|
+
xhr: function() {
|
89
|
+
var xhr;
|
90
|
+
xhr = $.ajaxSettings.xhr();
|
91
|
+
if ((xhr.upload != null) && _this.progress) {
|
92
|
+
xhr.upload.onprogress = function(event) {
|
93
|
+
if (event.lengthComputable) {
|
94
|
+
return _this.progress((event.position / event.total * 100).round(2));
|
95
|
+
}
|
96
|
+
};
|
97
|
+
}
|
98
|
+
return xhr;
|
99
|
+
}
|
100
|
+
});
|
101
|
+
if (this.resource != null) {
|
102
|
+
this.fill(this.resource, options);
|
103
|
+
delete this.resource;
|
104
|
+
}
|
105
|
+
if (this.action != null) {
|
106
|
+
this.container.attr('action', this.action);
|
107
|
+
this.container.attr('method', 'POST');
|
108
|
+
}
|
109
|
+
if (this.method != null) {
|
110
|
+
this.__markMethod(this.method);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
Form.prototype.unbind = function() {
|
115
|
+
return this.container.unbind('submit').find('input:submit,input:image,button:submit').unbind('click');
|
116
|
+
};
|
117
|
+
|
118
|
+
Form.prototype.fill = function(resource, options) {
|
119
|
+
var data, filler, url,
|
120
|
+
_this = this;
|
121
|
+
if (Object.isFunction(resource.build)) {
|
122
|
+
resource = resource.build();
|
123
|
+
}
|
124
|
+
this.__resource = resource;
|
125
|
+
if ((options != null ? options.decorator : void 0) != null) {
|
126
|
+
data = options.decorator(resource.data);
|
127
|
+
} else {
|
128
|
+
data = resource.data;
|
129
|
+
}
|
130
|
+
filler = function(data, scope) {
|
131
|
+
if (data.__joosy_form_filler_lock) {
|
132
|
+
return;
|
133
|
+
}
|
134
|
+
data.__joosy_form_filler_lock = true;
|
135
|
+
Object.each(data, function(property, val) {
|
136
|
+
var entity, i, input, key, _i, _len, _ref, _results;
|
137
|
+
key = _this.concatFieldName(scope, property);
|
138
|
+
input = _this.$fields.filter("[name='" + key + "']:not(:file),[name='" + (key.underscore()) + "']:not(:file),[name='" + (key.camelize(false)) + "']:not(:file)");
|
139
|
+
if (input.length > 0) {
|
140
|
+
if (input.is(':checkbox')) {
|
141
|
+
if (val) {
|
142
|
+
input.attr('checked', 'checked');
|
143
|
+
} else {
|
144
|
+
input.removeAttr('checked');
|
145
|
+
}
|
146
|
+
} else if (input.is(':radio')) {
|
147
|
+
input.filter("[value='" + val + "']").attr('checked', 'checked');
|
148
|
+
} else {
|
149
|
+
input.val(val);
|
150
|
+
}
|
151
|
+
}
|
152
|
+
if (val instanceof Joosy.Resources.RESTCollection) {
|
153
|
+
_ref = val.data;
|
154
|
+
_results = [];
|
155
|
+
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
156
|
+
entity = _ref[i];
|
157
|
+
_results.push(filler(entity.data, _this.concatFieldName(scope, "[" + property + "_attributes][" + i + "]")));
|
158
|
+
}
|
159
|
+
return _results;
|
160
|
+
} else if (val instanceof Joosy.Resources.REST) {
|
161
|
+
return filler(val.data, _this.concatFieldName(scope, "[" + property + "_attributes][0]"));
|
162
|
+
} else if (Object.isObject(val) || Object.isArray(val)) {
|
163
|
+
return filler(val, key);
|
164
|
+
} else {
|
165
|
+
|
166
|
+
}
|
167
|
+
});
|
168
|
+
return delete data.__joosy_form_filler_lock;
|
169
|
+
};
|
170
|
+
filler(data, resource.__entityName || options.resourceName);
|
171
|
+
$('input[name=_method]', this.container).remove();
|
172
|
+
if (resource.id()) {
|
173
|
+
this.__markMethod((options != null ? options.method : void 0) || 'PUT');
|
174
|
+
}
|
175
|
+
url = (options != null ? options.action : void 0) || (resource.id() != null ? resource.memberPath() : resource.collectionPath());
|
176
|
+
this.container.attr('action', url);
|
177
|
+
return this.container.attr('method', 'POST');
|
178
|
+
};
|
179
|
+
|
180
|
+
Form.prototype.submit = function() {
|
181
|
+
return this.container.submit();
|
182
|
+
};
|
183
|
+
|
184
|
+
Form.prototype.serialize = function(skipMethod) {
|
185
|
+
var data;
|
186
|
+
if (skipMethod == null) {
|
187
|
+
skipMethod = true;
|
188
|
+
}
|
189
|
+
data = this.container.serialize();
|
190
|
+
if (skipMethod) {
|
191
|
+
data = data.replace(/\&?\_method\=put/i, '');
|
192
|
+
}
|
193
|
+
return data;
|
194
|
+
};
|
195
|
+
|
196
|
+
Form.prototype.__success = function(response, status, xhr) {
|
197
|
+
var _ref;
|
198
|
+
if (xhr) {
|
199
|
+
return typeof this.success === "function" ? this.success(response) : void 0;
|
200
|
+
} else if ((200 <= (_ref = response.status) && _ref < 300)) {
|
201
|
+
return this.success(response.json);
|
202
|
+
} else {
|
203
|
+
return this.__error(response.json);
|
204
|
+
}
|
205
|
+
};
|
206
|
+
|
207
|
+
Form.prototype.__before = function(xhr, settings) {
|
208
|
+
if ((this.before == null) || this.before.apply(this, arguments) === true) {
|
209
|
+
return this.$fields.removeClass(this.invalidationClass);
|
210
|
+
}
|
211
|
+
};
|
212
|
+
|
213
|
+
Form.prototype.__error = function(data) {
|
214
|
+
var error, errors,
|
215
|
+
_this = this;
|
216
|
+
errors = (function() {
|
217
|
+
if (data.responseText) {
|
218
|
+
try {
|
219
|
+
return data = jQuery.parseJSON(data.responseText);
|
220
|
+
} catch (_error) {
|
221
|
+
error = _error;
|
222
|
+
return {};
|
223
|
+
}
|
224
|
+
} else {
|
225
|
+
return data;
|
226
|
+
}
|
227
|
+
})();
|
228
|
+
if ((this.error == null) || this.error(errors) === true) {
|
229
|
+
errors = this.__stringifyErrors(errors);
|
230
|
+
Object.each(errors, function(field, notifications) {
|
231
|
+
var input;
|
232
|
+
input = _this.findField(field).addClass(_this.invalidationClass);
|
233
|
+
return typeof _this.notification === "function" ? _this.notification(input, notifications) : void 0;
|
234
|
+
});
|
235
|
+
return errors;
|
236
|
+
}
|
237
|
+
return false;
|
238
|
+
};
|
239
|
+
|
240
|
+
Form.prototype.__debounce = function(xhr) {
|
241
|
+
this.debugAs(this, "debounce: pending_request == " + this.__pending_request);
|
242
|
+
if (this.__pending_request && this.debounce !== false) {
|
243
|
+
if (this.debounce || Joosy.Application.debounceForms) {
|
244
|
+
xhr.abort();
|
245
|
+
this.debugAs(this, "debounce: xhr aborted");
|
246
|
+
return true;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
return false;
|
250
|
+
};
|
251
|
+
|
252
|
+
Form.prototype.findField = function(field) {
|
253
|
+
return this.$fields.filter("[name='" + field + "']");
|
254
|
+
};
|
255
|
+
|
256
|
+
Form.prototype.__markMethod = function(method) {
|
257
|
+
if (method == null) {
|
258
|
+
method = 'PUT';
|
259
|
+
}
|
260
|
+
method = $('<input/>', {
|
261
|
+
type: 'hidden',
|
262
|
+
name: '_method',
|
263
|
+
value: method
|
264
|
+
});
|
265
|
+
return this.container.append(method);
|
266
|
+
};
|
267
|
+
|
268
|
+
Form.prototype.__stringifyErrors = function(errors) {
|
269
|
+
var result,
|
270
|
+
_this = this;
|
271
|
+
result = {};
|
272
|
+
Object.each(errors, function(field, notifications) {
|
273
|
+
var f, name, splited, _i, _len;
|
274
|
+
if (_this.substitutions[field] != null) {
|
275
|
+
field = _this.substitutions[field];
|
276
|
+
}
|
277
|
+
if (Object.isObject(notifications) || _this.isArrayOfObjects(notifications)) {
|
278
|
+
return Object.each(_this.__foldInlineEntities(notifications), function(key, value) {
|
279
|
+
return result[field + key] = value;
|
280
|
+
});
|
281
|
+
} else {
|
282
|
+
if (field.indexOf(".") !== -1) {
|
283
|
+
splited = field.split('.');
|
284
|
+
field = splited.shift();
|
285
|
+
if (_this.resourceName || _this.__resource) {
|
286
|
+
name = _this.resourceName || _this.__resource.__entityName;
|
287
|
+
field = name + ("[" + field + "]");
|
288
|
+
}
|
289
|
+
for (_i = 0, _len = splited.length; _i < _len; _i++) {
|
290
|
+
f = splited[_i];
|
291
|
+
field += "[" + f + "]";
|
292
|
+
}
|
293
|
+
} else if (_this.resourceName || _this.__resource) {
|
294
|
+
name = _this.resourceName || _this.__resource.__entityName;
|
295
|
+
field = name + ("[" + field + "]");
|
296
|
+
}
|
297
|
+
return result[field] = notifications;
|
298
|
+
}
|
299
|
+
});
|
300
|
+
return result;
|
301
|
+
};
|
302
|
+
|
303
|
+
Form.prototype.__foldInlineEntities = function(hash, scope, result) {
|
304
|
+
var _this = this;
|
305
|
+
if (scope == null) {
|
306
|
+
scope = "";
|
307
|
+
}
|
308
|
+
if (result == null) {
|
309
|
+
result = {};
|
310
|
+
}
|
311
|
+
Object.each(hash, function(key, value) {
|
312
|
+
if (Object.isObject(value) || _this.isArrayOfObjects(value)) {
|
313
|
+
return _this.__foldInlineEntities(value, "" + scope + "[" + key + "]", result);
|
314
|
+
} else {
|
315
|
+
return result["" + scope + "[" + key + "]"] = value;
|
316
|
+
}
|
317
|
+
});
|
318
|
+
return result;
|
319
|
+
};
|
320
|
+
|
321
|
+
Form.prototype.concatFieldName = function(wrapper, name) {
|
322
|
+
var items;
|
323
|
+
items = this.splitFieldName(wrapper).concat(this.splitFieldName(name));
|
324
|
+
return "" + items[0] + "[" + (items.slice(1).join('][')) + "]";
|
325
|
+
};
|
326
|
+
|
327
|
+
Form.prototype.splitFieldName = function(name) {
|
328
|
+
var first, items;
|
329
|
+
items = name.split('][');
|
330
|
+
first = items[0].split('[');
|
331
|
+
if (first.length === 2) {
|
332
|
+
if (first[0].isBlank()) {
|
333
|
+
items.splice(0, 1, first[1]);
|
334
|
+
} else {
|
335
|
+
items.splice(0, 1, first[0], first[1]);
|
336
|
+
}
|
337
|
+
items[items.length - 1] = items[items.length - 1].split(']')[0];
|
338
|
+
}
|
339
|
+
return items;
|
340
|
+
};
|
341
|
+
|
342
|
+
Form.prototype.isArrayOfObjects = function(array) {
|
343
|
+
return Object.isArray(array) && array.every(function(elem) {
|
344
|
+
return Object.isObject(elem);
|
345
|
+
});
|
346
|
+
};
|
347
|
+
|
348
|
+
return Form;
|
349
|
+
|
350
|
+
})(Joosy.Module);
|
351
|
+
|
352
|
+
|
353
|
+
/*** src/joosy/extensions/form/helpers/form ***/
|
354
|
+
|
355
|
+
Joosy.helpers('Application', function() {
|
356
|
+
var Form, description, input,
|
357
|
+
_this = this;
|
358
|
+
description = function(resource, method, extendIds, idSuffix) {
|
359
|
+
var id;
|
360
|
+
if (Joosy.Module.hasAncestor(resource.constructor, Joosy.Resources.Base)) {
|
361
|
+
id = resource.id();
|
362
|
+
resource = resource.__entityName;
|
363
|
+
}
|
364
|
+
return {
|
365
|
+
name: resource + ("" + (method.match(/^\[.*\]$/) ? method : "[" + method + "]")),
|
366
|
+
id: resource + (id && extendIds ? '_' + id : '') + ("_" + (method.parameterize().underscore())) + (idSuffix ? '_' + idSuffix : '')
|
367
|
+
};
|
368
|
+
};
|
369
|
+
input = function(type, resource, method, options) {
|
370
|
+
var d;
|
371
|
+
if (options == null) {
|
372
|
+
options = {};
|
373
|
+
}
|
374
|
+
d = description(resource, method, options.extendIds, options.idSuffix);
|
375
|
+
delete options.extendIds;
|
376
|
+
delete options.idSuffix;
|
377
|
+
return _this.tag('input', Joosy.Module.merge({
|
378
|
+
type: type,
|
379
|
+
name: d.name,
|
380
|
+
id: d.id
|
381
|
+
}, options));
|
382
|
+
};
|
383
|
+
Form = (function() {
|
384
|
+
function Form(context, resource, options) {
|
385
|
+
this.context = context;
|
386
|
+
this.resource = resource;
|
387
|
+
this.options = options;
|
388
|
+
}
|
389
|
+
|
390
|
+
Form.prototype.label = function(method, options, content) {
|
391
|
+
if (options == null) {
|
392
|
+
options = {};
|
393
|
+
}
|
394
|
+
if (content == null) {
|
395
|
+
content = '';
|
396
|
+
}
|
397
|
+
if (!Object.isObject(options)) {
|
398
|
+
content = options;
|
399
|
+
options = {};
|
400
|
+
}
|
401
|
+
return this.context.label(this.resource, method, Joosy.Module.merge({
|
402
|
+
extendIds: this.options.extendIds
|
403
|
+
}, options), content);
|
404
|
+
};
|
405
|
+
|
406
|
+
Form.prototype.radioButton = function(method, tagValue, options) {
|
407
|
+
if (options == null) {
|
408
|
+
options = {};
|
409
|
+
}
|
410
|
+
return this.context.radioButton(this.resource, method, tagValue, Joosy.Module.merge({
|
411
|
+
extendIds: this.options.extendIds
|
412
|
+
}, options));
|
413
|
+
};
|
414
|
+
|
415
|
+
Form.prototype.textArea = function(method, options) {
|
416
|
+
if (options == null) {
|
417
|
+
options = {};
|
418
|
+
}
|
419
|
+
return this.context.textArea(this.resource, method, Joosy.Module.merge({
|
420
|
+
extendIds: this.options.extendIds
|
421
|
+
}, options));
|
422
|
+
};
|
423
|
+
|
424
|
+
Form.prototype.checkBox = function(method, options, checkedValue, uncheckedValue) {
|
425
|
+
if (options == null) {
|
426
|
+
options = {};
|
427
|
+
}
|
428
|
+
if (checkedValue == null) {
|
429
|
+
checkedValue = 1;
|
430
|
+
}
|
431
|
+
if (uncheckedValue == null) {
|
432
|
+
uncheckedValue = 0;
|
433
|
+
}
|
434
|
+
return this.context.checkBox(this.resource, method, Joosy.Module.merge({
|
435
|
+
extendIds: this.options.extendIds
|
436
|
+
}, options), checkedValue, uncheckedValue);
|
437
|
+
};
|
438
|
+
|
439
|
+
Form.prototype.select = function(method, options, htmlOptions) {
|
440
|
+
if (options == null) {
|
441
|
+
options = {};
|
442
|
+
}
|
443
|
+
if (htmlOptions == null) {
|
444
|
+
htmlOptions = {};
|
445
|
+
}
|
446
|
+
return this.context.select(this.resource, method, options, Joosy.Module.merge({
|
447
|
+
extendIds: this.options.extendIds
|
448
|
+
}, htmlOptions));
|
449
|
+
};
|
450
|
+
|
451
|
+
return Form;
|
452
|
+
|
453
|
+
})();
|
454
|
+
['text', 'file', 'hidden', 'password'].each(function(type) {
|
455
|
+
return Form.prototype[type + 'Field'] = function(method, options) {
|
456
|
+
if (options == null) {
|
457
|
+
options = {};
|
458
|
+
}
|
459
|
+
return this.context[type + 'Field'](this.resource, method, Joosy.Module.merge({
|
460
|
+
extendIds: this.options.extendIds
|
461
|
+
}, options));
|
462
|
+
};
|
463
|
+
});
|
464
|
+
this.formFor = function(resource, options, block) {
|
465
|
+
var form, uuid;
|
466
|
+
if (options == null) {
|
467
|
+
options = {};
|
468
|
+
}
|
469
|
+
if (Object.isFunction(options)) {
|
470
|
+
block = options;
|
471
|
+
options = {};
|
472
|
+
}
|
473
|
+
uuid = Joosy.uuid();
|
474
|
+
form = this.tag('form', Joosy.Module.merge(options.html || {}, {
|
475
|
+
id: uuid
|
476
|
+
}), block != null ? block.call(this, new Form(this, resource, options)) : void 0);
|
477
|
+
if (typeof this.onRefresh === "function") {
|
478
|
+
this.onRefresh(function() {
|
479
|
+
return Joosy.Form.attach('#' + uuid, Joosy.Module.merge(options, {
|
480
|
+
resource: resource
|
481
|
+
}));
|
482
|
+
});
|
483
|
+
}
|
484
|
+
return form;
|
485
|
+
};
|
486
|
+
this.label = function(resource, method, options, content) {
|
487
|
+
var d;
|
488
|
+
if (options == null) {
|
489
|
+
options = {};
|
490
|
+
}
|
491
|
+
if (content == null) {
|
492
|
+
content = '';
|
493
|
+
}
|
494
|
+
if (!Object.isObject(options)) {
|
495
|
+
content = options;
|
496
|
+
options = {};
|
497
|
+
}
|
498
|
+
d = description(resource, method, options.extendIds);
|
499
|
+
delete options.extendIds;
|
500
|
+
return this.tag('label', Joosy.Module.merge(options, {
|
501
|
+
"for": d.id
|
502
|
+
}), content);
|
503
|
+
};
|
504
|
+
['text', 'file', 'hidden', 'password'].each(function(type) {
|
505
|
+
return _this[type + 'Field'] = function(resource, method, options) {
|
506
|
+
if (options == null) {
|
507
|
+
options = {};
|
508
|
+
}
|
509
|
+
return input(type, resource, method, options);
|
510
|
+
};
|
511
|
+
});
|
512
|
+
this.radioButton = function(resource, method, tagValue, options) {
|
513
|
+
if (options == null) {
|
514
|
+
options = {};
|
515
|
+
}
|
516
|
+
return input('radio', resource, method, Joosy.Module.merge({
|
517
|
+
value: tagValue,
|
518
|
+
idSuffix: tagValue
|
519
|
+
}, options));
|
520
|
+
};
|
521
|
+
this.checkBox = function(resource, method, options, checkedValue, uncheckedValue) {
|
522
|
+
var box, spy;
|
523
|
+
if (options == null) {
|
524
|
+
options = {};
|
525
|
+
}
|
526
|
+
if (checkedValue == null) {
|
527
|
+
checkedValue = 1;
|
528
|
+
}
|
529
|
+
if (uncheckedValue == null) {
|
530
|
+
uncheckedValue = 0;
|
531
|
+
}
|
532
|
+
spy = this.tag('input', Joosy.Module.merge({
|
533
|
+
name: description(resource, method).name,
|
534
|
+
value: uncheckedValue,
|
535
|
+
type: 'hidden'
|
536
|
+
}));
|
537
|
+
box = input('checkbox', resource, method, Joosy.Module.merge({
|
538
|
+
value: checkedValue
|
539
|
+
}, options));
|
540
|
+
return spy + box;
|
541
|
+
};
|
542
|
+
this.select = function(resource, method, options, htmlOptions) {
|
543
|
+
var extendIds, key, opts, val,
|
544
|
+
_this = this;
|
545
|
+
if (Object.isObject(options)) {
|
546
|
+
opts = [];
|
547
|
+
for (key in options) {
|
548
|
+
val = options[key];
|
549
|
+
opts.push([val, key]);
|
550
|
+
}
|
551
|
+
} else {
|
552
|
+
opts = options;
|
553
|
+
}
|
554
|
+
if (htmlOptions.includeBlank) {
|
555
|
+
delete htmlOptions.includeBlank;
|
556
|
+
opts.unshift(['', '']);
|
557
|
+
}
|
558
|
+
opts = opts.reduce(function(str, vals) {
|
559
|
+
var params;
|
560
|
+
params = Object.isArray(vals) ? [
|
561
|
+
'option', {
|
562
|
+
value: vals[1]
|
563
|
+
}, vals[0]
|
564
|
+
] : ['option', {}, vals];
|
565
|
+
if (htmlOptions.value === (Object.isArray(vals) ? vals[1] : vals)) {
|
566
|
+
params[1].selected = 'selected';
|
567
|
+
}
|
568
|
+
return str += _this.tag.apply(_this, params);
|
569
|
+
}, '');
|
570
|
+
extendIds = htmlOptions.extendIds;
|
571
|
+
delete htmlOptions.value;
|
572
|
+
delete htmlOptions.extendIds;
|
573
|
+
return this.tag('select', Joosy.Module.merge(description(resource, method, extendIds), htmlOptions), opts);
|
574
|
+
};
|
575
|
+
return this.textArea = function(resource, method, options) {
|
576
|
+
var extendIds, value;
|
577
|
+
if (options == null) {
|
578
|
+
options = {};
|
579
|
+
}
|
580
|
+
value = options.value;
|
581
|
+
extendIds = options.extendIds;
|
582
|
+
delete options.value;
|
583
|
+
delete options.extendIds;
|
584
|
+
return this.tag('textarea', Joosy.Module.merge(description(resource, method, extendIds), options), value);
|
585
|
+
};
|
586
|
+
});
|
587
|
+
|
588
|
+
|
589
|
+
/*** src/joosy/extensions/form/index ***/
|
590
|
+
|
591
|
+
|
592
|
+
;
|