manatee 0.0.1.pre1

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.mdown +55 -0
  5. data/Rakefile +7 -0
  6. data/app/assets/javascripts/manatee.js +1 -0
  7. data/app/assets/javascripts/manatee/helpers.js +4 -0
  8. data/app/assets/javascripts/manatee/helpers/asset_tag.jsh.coffee +138 -0
  9. data/app/assets/javascripts/manatee/helpers/asset_url.jsh.coffee +90 -0
  10. data/app/assets/javascripts/manatee/helpers/csrf.jsh.coffee +5 -0
  11. data/app/assets/javascripts/manatee/helpers/date.jsh.coffee +54 -0
  12. data/app/assets/javascripts/manatee/helpers/debug.jsh.coffee +2 -0
  13. data/app/assets/javascripts/manatee/helpers/form.jsh.coffee +25 -0
  14. data/app/assets/javascripts/manatee/helpers/form_builder.jsh.coffee +24 -0
  15. data/app/assets/javascripts/manatee/helpers/form_options.jsh.coffee +267 -0
  16. data/app/assets/javascripts/manatee/helpers/form_tag.jsh.coffee +204 -0
  17. data/app/assets/javascripts/manatee/helpers/javascript.jsh.coffee +15 -0
  18. data/app/assets/javascripts/manatee/helpers/number.jsh.coffee +58 -0
  19. data/app/assets/javascripts/manatee/helpers/sanitize.jsh.coffee +5 -0
  20. data/app/assets/javascripts/manatee/helpers/tag.jsh.coffee +58 -0
  21. data/app/assets/javascripts/manatee/helpers/text.jsh.coffee +12 -0
  22. data/app/assets/javascripts/manatee/helpers/translation.jsh.coffee +7 -0
  23. data/app/assets/javascripts/manatee/helpers/url.jsh.coffee +36 -0
  24. data/app/assets/javascripts/manatee/helpers/util.jsh.coffee +41 -0
  25. data/app/assets/javascripts/manatee/rails_helpers.js +2 -0
  26. data/app/assets/javascripts/manatee/rails_helpers/routes.js.erb +195 -0
  27. data/app/assets/javascripts/manatee/rails_routes.js +1 -0
  28. data/app/assets/javascripts/manatee/renderer.js.erb +53 -0
  29. data/app/assets/javascripts/manatee_railsless.js +1 -0
  30. data/lib/manatee.rb +87 -0
  31. data/lib/manatee/config.rb +51 -0
  32. data/lib/manatee/handler.rb +45 -0
  33. data/lib/manatee/rails.rb +6 -0
  34. data/lib/manatee/rails/extensions.rb +23 -0
  35. data/lib/manatee/rails/handler.rb +16 -0
  36. data/lib/manatee/rails/hash_visitor.rb +35 -0
  37. data/lib/manatee/rails/helper.rb +9 -0
  38. data/lib/manatee/rails/resolver.rb +70 -0
  39. data/lib/manatee/rails/routes_compiler.rb +34 -0
  40. data/lib/manatee/sprockets.rb +8 -0
  41. data/lib/manatee/sprockets/jsh_processor_2x.rb +32 -0
  42. data/lib/manatee/sprockets/jsh_processor_3x.rb +31 -0
  43. data/lib/manatee/version.rb +3 -0
  44. data/manatee.gemspec +32 -0
  45. data/test/example/renderer.js.erb +8 -0
  46. data/test/example/translations.en.yml +410 -0
  47. data/test/example/views/index.jst.eco +12 -0
  48. data/test/helpers/asset_tag_test.rb +175 -0
  49. data/test/helpers/asset_url_test.rb +349 -0
  50. data/test/helpers/form_options_test.rb +718 -0
  51. data/test/helpers/form_tag_test.rb +387 -0
  52. data/test/helpers/javascript_test.rb +39 -0
  53. data/test/helpers/number_test.rb +111 -0
  54. data/test/helpers/tag_test.rb +71 -0
  55. data/test/support/dom_assertion.rb +49 -0
  56. data/test/support/view_test.rb +91 -0
  57. data/test/test_helper.rb +17 -0
  58. metadata +213 -0
@@ -0,0 +1,15 @@
1
+ javascriptCDATA = (content) ->
2
+ if typeof content == 'function'
3
+ content = content.call(this)
4
+ "\n//#{@CDATA( "\n" + content + "\n//" )}\n"
5
+
6
+ helper 'javascriptTag', (content_or_options, options_or_content = {}) ->
7
+ [content, options] = @_contentOrOptions content_or_options, options_or_content
8
+ @contentTag 'script', javascriptCDATA.call(this, content), options
9
+
10
+ helper 'escapeJavascript', (content) ->
11
+ if content
12
+ content.replace(/\\/g, '\\\\').replace(/<\//g, '<\\\/').replace(/\r\n/g, '\\n').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\"/g, '\\"').replace(/\'/g, "\\'").replace(/\u2028/g, "&#x2028;").replace(/\u2029/g, "&#x2029;")
13
+ else
14
+ ''
15
+ alias 'j', 'escapeJavascript'
@@ -0,0 +1,58 @@
1
+ # numberHelperBuilder = (_translation_key, _i18n_function) ->
2
+ # i18n_function = _i18n_function.bind I18n
3
+ # translation_key = _translation_key
4
+ # (number, options = {}) ->
5
+ # return null if number == false || number == null || number == undefined
6
+ # i18n_options = if options['locale']
7
+ # translated_options = @_clone @translate( translation_key, locale: options['locale'] )
8
+ # translated_options['negative_format'] = options['negative_format']
9
+ # translated_options
10
+ # else
11
+ # @_clone options
12
+ #
13
+ # result = if number < 0 && i18n_options['negative_format']
14
+ # i18n_options['format'] = i18n_options['negative_format']
15
+ # i18n_function(-number, i18n_options)
16
+ # else
17
+ # i18n_function(number, i18n_options)
18
+ #
19
+ # if i18n_options['escape'] || i18n_options['escape'] == undefined
20
+ # @htmlEscape result
21
+ # else
22
+ # result
23
+
24
+ numberHelperBuilder = (_translation_key, _i18n_function) ->
25
+ i18n_function = _i18n_function.bind I18n
26
+ translation_key = _translation_key
27
+ (number, options = {}) ->
28
+ return null if number == false || number == null || number == undefined
29
+ i18n_options = if options['locale']
30
+ @_clone @translate( translation_key, locale: options['locale'] )
31
+ else
32
+ opts = @_clone options
33
+ for key, value of @translate(translation_key)
34
+ opts[key] = value if opts[key] == undefined
35
+ opts
36
+
37
+ result = if number < 0 && options['negative_format']
38
+ i18n_options['format'] = options['negative_format']
39
+ i18n_function(-number, i18n_options)
40
+ else
41
+ i18n_function(number, i18n_options)
42
+
43
+ if options['escape'] || options['escape'] == undefined
44
+ @htmlEscape result
45
+ else
46
+ result
47
+
48
+
49
+ helper 'numberToCurrency', numberHelperBuilder.call(this, 'number.currency.format', I18n.toCurrency )
50
+
51
+ # TODO: Add number helper tests and fix I18n-js gem
52
+ helper 'numberToHuman', numberHelperBuilder.call(this, 'number.human.decimal_units', I18n.toNumber )
53
+ helper 'numberToHumanSize', numberHelperBuilder.call(this, 'number.human.storage_units', I18n.toHumanSize )
54
+ helper 'numberToPercentage', numberHelperBuilder.call(this, 'number.percentage.format', I18n.toPercentage )
55
+
56
+ # number_to_phone
57
+ # number_with_delimiter
58
+ # number_with_precision
@@ -0,0 +1,5 @@
1
+
2
+ # sanitize
3
+ # sanitize_css
4
+ # strip_links
5
+ # strip_tags
@@ -0,0 +1,58 @@
1
+ writeTagAttributes = (options) ->
2
+ tag = ''
3
+ if options['data']
4
+ for key, value of options['data']
5
+ options['data-' + key] = value
6
+ options['data'] = undefined
7
+ for key, value of options
8
+ key = key.replace(/\_/g, '-')
9
+ unless typeof value == 'undefined'
10
+ if value == true
11
+ tag += " " + key + "=" + '"' + key + '"'
12
+ else
13
+ value = if typeof value == 'string'
14
+ value.replace(/\"/g, '&#34;').replace(/\'/g, '&#39;')
15
+ else
16
+ if value == null
17
+ value = ''
18
+ else
19
+ value.toString()
20
+ tag += " " + key + "=" + '"' + value + '"'
21
+ tag
22
+
23
+ helper 'tag', (name, options = {}) ->
24
+ options = @_clone options
25
+ tag = "<" + name
26
+ if arguments.length > 1
27
+ tag += writeTagAttributes(options)
28
+ tag + '/>'
29
+
30
+ helper 'contentTag', (name, content_or_options = '', options_or_content = '') ->
31
+ [content, options] = if arguments.length == 1
32
+ ['', {}]
33
+ else if arguments.length == 2
34
+ if content_or_options != null && typeof content_or_options == 'object'
35
+ [ options_or_content || '', @_clone(content_or_options) ]
36
+ else
37
+ [ content_or_options || '', {} ]
38
+ else
39
+ if content_or_options != null && typeof content_or_options == 'object'
40
+ [ options_or_content || '', @_clone(content_or_options) ]
41
+ else
42
+ [ content_or_options || '', @_clone(options_or_content) ]
43
+
44
+ tag = "<" + name
45
+ tag += writeTagAttributes(options)
46
+ tag += '>'
47
+
48
+ if typeof content == 'function'
49
+ tag += content.call(this)
50
+ else
51
+ tag += content
52
+
53
+ tag + '</' + name + '>'
54
+
55
+ helper 'CDATA', (content) ->
56
+ if typeof content == 'function'
57
+ content = content.call(this)
58
+ "<![CDATA[" + content + "]]>"
@@ -0,0 +1,12 @@
1
+
2
+ # concat
3
+ # current_cycle
4
+ # cycle
5
+ # excerpt
6
+ # highlight
7
+ # pluralize
8
+ # reset_cycle
9
+ # safe_concat
10
+ # simple_format
11
+ # truncate
12
+ # word_wrap
@@ -0,0 +1,7 @@
1
+ helper 'translate', (key, options) ->
2
+ I18n.t key, options
3
+ alias 't', 'translate'
4
+
5
+ helper 'localize', (key, options) ->
6
+ I18n.l key, options
7
+ alias 'l', 'localize'
@@ -0,0 +1,36 @@
1
+ helper 'urlFor', (source, options = {}) ->
2
+ if @_context.forceDomain
3
+ options['domain'] ||= @_context.domain.app
4
+ if options['domain']
5
+ source = options['domain'] + source
6
+ if options['format']
7
+ source += '.' + options['format']
8
+ source
9
+
10
+ helper 'linkTo', (name, url, options = {}) ->
11
+ options = @_clone options
12
+ options['href'] ||= url
13
+ if options['method']
14
+ options['rel'] = 'nofollow'
15
+ options['data-method'] = options['method']
16
+ options['method'] = undefined
17
+ if options['remote']
18
+ options['data-remote'] = 'true'
19
+ options['remote'] = undefined
20
+ @contentTag 'a', name, options
21
+
22
+ helper 'linkToIf', (condition, name, url, options = {}) ->
23
+ if condition then @linkTo(name, url, options) else ''
24
+
25
+ helper 'linkToUnless', (condition, name, url, options = {}) ->
26
+ @linkToIf !condition, name, url, options
27
+
28
+ helper 'contentLinkTo', (url, options_or_content, content_or_options) ->
29
+ [ content, options ] = @_contentOrOptions options_or_content, content_or_options
30
+ @linkTo content, url, options
31
+
32
+
33
+ # button_to
34
+ # mail_to
35
+ # current_page?
36
+ # link_to_unless_current
@@ -0,0 +1,41 @@
1
+ helper '_contentOrOptions', (content_or_options, options_or_content, default_content) ->
2
+ if content_or_options != null && typeof content_or_options == 'object'
3
+ [ options_or_content || default_content, @_clone(content_or_options) ]
4
+ else
5
+ [ content_or_options || default_content, @_clone( options_or_content || {} ) ]
6
+
7
+ htmlEscapeMap = { '&': '&amp;', '<': '&lt;', '>': '&gt;' }
8
+ escapeHTMLCallback = (key) ->
9
+ htmlEscapeMap[key] || key;
10
+
11
+ helper '_clone', (object) ->
12
+ return object if object == null || typeof object != "object"
13
+ cloned_object = object.constructor()
14
+ for attr of object
15
+ cloned_object[attr] = object[attr] if object.hasOwnProperty(attr)
16
+ cloned_object
17
+
18
+ helper 'htmlEscape', (string) ->
19
+ string.replace(/[&<>]/g, escapeHTMLCallback);
20
+
21
+ helper 'htmlEscapeAttributes', (attributes) ->
22
+ escaped_attributes = {}
23
+ for index, value of attributes
24
+ if typeof value == 'string'
25
+ escaped_attributes[index] = @htmlEscape(value)
26
+ else if typeof value == 'object'
27
+ escaped_attributes[index] = @htmlEscapeAttributes(value)
28
+ else
29
+ escaped_attributes[index] = value
30
+ escaped_attributes
31
+
32
+ helper 'underscore', (string) ->
33
+ results = []
34
+ sub_strings = string.split '::'
35
+ for index, sub_string of sub_strings
36
+ replaced = sub_string.replace( /([A-Z0-9]+)/g, ( (str) -> '_' + str.toLowerCase() ) )
37
+ if (/([A-Z0-9])/).test(sub_string[0])
38
+ results.push replaced.slice(1)
39
+ else
40
+ results.push replaced
41
+ results.join '/'
@@ -0,0 +1,2 @@
1
+ //= require ./helpers
2
+ //= require_tree ./rails_helpers
@@ -0,0 +1,195 @@
1
+ var <%= Manatee.router_namespace %> = function(){
2
+
3
+ var RoutesVisitor = function(){
4
+
5
+ function call(options, node){
6
+ this.options = options;
7
+ this.consumed = {};
8
+ return this.visit(node);
9
+ }
10
+
11
+ function visit(node){
12
+ return this['visit'+node.type](node);
13
+ }
14
+
15
+ function nary(node){
16
+ var result = '';
17
+ for(var n in node.children){
18
+ result += this.visit(n);
19
+ }
20
+ return result;
21
+ }
22
+
23
+ function binary(node){
24
+ return this.visit( node.children[0] ) + this.visit( node.children[1] );
25
+ }
26
+
27
+ function unary(node){
28
+ return this.visit(node.children[0]);
29
+ }
30
+
31
+ function terminal(node){
32
+ return node.value;
33
+ }
34
+
35
+ function visitCAT(node){
36
+ return this.binary(node);
37
+ }
38
+
39
+ function visitOR(node){
40
+ return this.nary(node);
41
+ }
42
+
43
+ function visitGROUP(node){
44
+ if(compareObjects(this.options, this.consumed)){
45
+ return '';
46
+ } else {
47
+ var route = this.visit(node.children[0]);
48
+
49
+ if(this.hasNullByte(route)){
50
+ return '';
51
+ } else {
52
+ return route;
53
+ }
54
+ }
55
+ }
56
+
57
+ function visitSTAR(node){
58
+ return this.unary(node);
59
+ }
60
+
61
+ function visitLITERAL(node){
62
+ return this.terminal(node);
63
+ }
64
+
65
+ function visitSYMBOL(node){
66
+ var key = node.value.substring(1, node.value.lenght);
67
+ var value = this.options[key];
68
+
69
+ if(value){
70
+ this.consumed[key] = value;
71
+ return encodeURIComponent(value.toString());
72
+ } else {
73
+ return "\0";
74
+ }
75
+ }
76
+
77
+ function visitSLASH(node){
78
+ return this.terminal(node);
79
+ }
80
+
81
+ function visitDOT(node){
82
+ return this.terminal(node);
83
+ }
84
+
85
+ // Utils
86
+
87
+ function hasNullByte(string){
88
+ for(var i in string){
89
+ if( string[i] == "\0" ) return true;
90
+ }
91
+ return false;
92
+ }
93
+
94
+ function compareObjects(obj1, obj2){
95
+ return objectCount(obj1) == objectCount(obj2);
96
+ }
97
+
98
+ function objectCount(obj){
99
+ var count = 0;
100
+ for( var i in obj ){ count++; }
101
+ return count;
102
+ }
103
+
104
+ return {
105
+ call: call,
106
+ hasNullByte: hasNullByte,
107
+ compareObjects: compareObjects,
108
+ objectCount: objectCount,
109
+
110
+ binary: binary,
111
+ nary: nary,
112
+ terminal: terminal,
113
+ unary: unary,
114
+ visit: visit,
115
+ visitCAT: visitCAT,
116
+ visitDOT: visitDOT,
117
+ visitGROUP: visitGROUP,
118
+ visitLITERAL: visitLITERAL,
119
+ visitOR: visitOR,
120
+ visitSLASH: visitSLASH,
121
+ visitSTAR: visitSTAR,
122
+ visitSYMBOL: visitSYMBOL
123
+ };
124
+ }();
125
+
126
+ function pathFunctionBuilder(route){
127
+ return function(){
128
+ if( arguments.length != this.required_names.length && arguments.length != this.required_names.length + 1){
129
+ throw 'Argument Error';
130
+ } else {
131
+ var options = arguments[this.required_names.length];
132
+
133
+ var params = {};
134
+ for(var i in this.required_names){
135
+ var key = this.required_names[i];
136
+ params[key] = arguments[i];
137
+ }
138
+
139
+ var optionalMap = {};
140
+ for(var i in this.optional_names){
141
+ optionalMap[this.optional_names[i]] = true;
142
+ }
143
+
144
+ var queryParams = {};
145
+ for(var k in options){
146
+ if(optionalMap[k]){
147
+ params[k] = options[k];
148
+ } else {
149
+ queryParams[k] = options[k];
150
+ }
151
+ }
152
+
153
+ return <%= Manatee.renderer_namespace %>.urlFor(this._router._visitor.call(params, this.ast));
154
+ }
155
+ };
156
+ }
157
+
158
+ function urlHelper(){
159
+ return this._router._fullDomain + this.path.apply(this, arguments);
160
+ }
161
+
162
+ var compiledRoutes = <%= Manatee::Rails::RoutesCompiler.new.compile.to_json %>;
163
+
164
+ var routes = {
165
+ _routes: compiledRoutes,
166
+ _visitor: RoutesVisitor,
167
+ _fullDomain: <%= Manatee.full_domain.inspect %>
168
+ }
169
+
170
+ for( var i in compiledRoutes ){
171
+ var route = compiledRoutes[i];
172
+
173
+ route.url = urlHelper;
174
+ route.path = pathFunctionBuilder(route);
175
+ route._router = routes;
176
+
177
+ routes[route.helper_name] = route;
178
+
179
+ (function(r){
180
+ var route = r;
181
+
182
+ <%= Manatee.renderer_namespace %>.helper(route.helper_name+'Path', function(){
183
+ return route.path.apply(route, arguments);
184
+ });
185
+
186
+ <%= Manatee.renderer_namespace %>.helper(route.helper_name+'Url', function(){
187
+ return route.url.apply(route, arguments);
188
+ });
189
+ })(route);
190
+
191
+ }
192
+
193
+ return routes;
194
+
195
+ }();
@@ -0,0 +1 @@
1
+ //= require ./rails_helpers/routes