ultimate-helpers 0.1.0.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.
Files changed (101) hide show
  1. data/.gitignore +8 -0
  2. data/.rvmrc +1 -0
  3. data/.rvmrc.example +1 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +126 -0
  6. data/LICENSE +19 -0
  7. data/README.md +25 -0
  8. data/Rakefile +1 -0
  9. data/app/assets/javascripts/ultimate/base.js.coffee +11 -0
  10. data/app/assets/javascripts/ultimate/helpers/asset_tag.js.coffee +82 -0
  11. data/app/assets/javascripts/ultimate/helpers/base.js.coffee +3 -0
  12. data/app/assets/javascripts/ultimate/helpers/decor.js.coffee +19 -0
  13. data/app/assets/javascripts/ultimate/helpers/form_options.js.coffee +106 -0
  14. data/app/assets/javascripts/ultimate/helpers/form_tag.js.coffee +175 -0
  15. data/app/assets/javascripts/ultimate/helpers/javascript.js.coffee +31 -0
  16. data/app/assets/javascripts/ultimate/helpers/number.js.coffee +600 -0
  17. data/app/assets/javascripts/ultimate/helpers/record_tag.js.coffee +81 -0
  18. data/app/assets/javascripts/ultimate/helpers/tag.js.coffee +91 -0
  19. data/app/assets/javascripts/ultimate/helpers/url.js.coffee +152 -0
  20. data/app/assets/javascripts/ultimate/helpers.js.coffee +122 -0
  21. data/app/assets/javascripts/ultimate/jquery.base.js.coffee +85 -0
  22. data/app/assets/javascripts/ultimate/underscore/underscore.inflection.js +176 -0
  23. data/app/assets/javascripts/ultimate/underscore/underscore.js +1204 -0
  24. data/app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee +158 -0
  25. data/app/assets/javascripts/ultimate/underscore/underscore.string.js +600 -0
  26. data/config/routes.rb +2 -0
  27. data/lib/ultimate/helpers/engine.rb +7 -0
  28. data/lib/ultimate/helpers/version.rb +5 -0
  29. data/lib/ultimate/helpers.rb +8 -0
  30. data/scripts/rails +8 -0
  31. data/test/dummy/Rakefile +7 -0
  32. data/test/dummy/_emfile +18 -0
  33. data/test/dummy/app/assets/images/.gitkeep +0 -0
  34. data/test/dummy/app/assets/javascripts/application.js +3 -0
  35. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  36. data/test/dummy/app/assets/stylesheets/global/forms.css.scss +65 -0
  37. data/test/dummy/app/assets/stylesheets/global/layout/footer.scss +18 -0
  38. data/test/dummy/app/assets/stylesheets/global/layout/header.scss +13 -0
  39. data/test/dummy/app/assets/stylesheets/global/layout/main-menu.scss +68 -0
  40. data/test/dummy/app/assets/stylesheets/global/layout.css.scss +46 -0
  41. data/test/dummy/app/assets/stylesheets/global/reserved.css.scss +79 -0
  42. data/test/dummy/app/assets/stylesheets/global/reset.css.scss +85 -0
  43. data/test/dummy/app/assets/stylesheets/global/structures.css.scss +9 -0
  44. data/test/dummy/app/assets/stylesheets/import/base.scss +36 -0
  45. data/test/dummy/app/assets/stylesheets/plugins/qunit.css.scss +3 -0
  46. data/test/dummy/app/controllers/application_controller.rb +4 -0
  47. data/test/dummy/app/controllers/main_controller.rb +8 -0
  48. data/test/dummy/app/helpers/application_helper.rb +2 -0
  49. data/test/dummy/app/mailers/.gitkeep +0 -0
  50. data/test/dummy/app/models/.gitkeep +0 -0
  51. data/test/dummy/app/views/application/_footer.html.haml +3 -0
  52. data/test/dummy/app/views/application/_header.html.haml +4 -0
  53. data/test/dummy/app/views/application/_main_menu.html.haml +11 -0
  54. data/test/dummy/app/views/layouts/application.html.haml +24 -0
  55. data/test/dummy/app/views/main/index.html.haml +13 -0
  56. data/test/dummy/app/views/main/qunit.html.haml +7 -0
  57. data/test/dummy/config/application.rb +58 -0
  58. data/test/dummy/config/boot.rb +10 -0
  59. data/test/dummy/config/database.yml +25 -0
  60. data/test/dummy/config/environment.rb +5 -0
  61. data/test/dummy/config/environments/development.rb +40 -0
  62. data/test/dummy/config/environments/production.rb +67 -0
  63. data/test/dummy/config/environments/test.rb +37 -0
  64. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/test/dummy/config/initializers/inflections.rb +15 -0
  66. data/test/dummy/config/initializers/mime_types.rb +5 -0
  67. data/test/dummy/config/initializers/qunit-rails.rb +1 -0
  68. data/test/dummy/config/initializers/secret_token.rb +7 -0
  69. data/test/dummy/config/initializers/session_store.rb +8 -0
  70. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  71. data/test/dummy/config/locales/en.yml +5 -0
  72. data/test/dummy/config/routes.rb +63 -0
  73. data/test/dummy/config.ru +4 -0
  74. data/test/dummy/log/.gitkeep +0 -0
  75. data/test/dummy/public/404.html +26 -0
  76. data/test/dummy/public/422.html +26 -0
  77. data/test/dummy/public/500.html +25 -0
  78. data/test/dummy/public/favicon.ico +0 -0
  79. data/test/dummy/script/rails +6 -0
  80. data/test/javascripts/all_tests.js.coffee +12 -0
  81. data/test/javascripts/test_helper.js.coffee +3 -0
  82. data/test/javascripts/tests/helpers/asset_tag_test.js.coffee +51 -0
  83. data/test/javascripts/tests/helpers/form_options_test.js.coffee +170 -0
  84. data/test/javascripts/tests/helpers/form_tag_test.js.coffee +226 -0
  85. data/test/javascripts/tests/helpers/javascript_test.js.coffee +25 -0
  86. data/test/javascripts/tests/helpers/number_test.js.coffee +250 -0
  87. data/test/javascripts/tests/helpers/record_tag_test.js.coffee +60 -0
  88. data/test/javascripts/tests/helpers/tag_test.js.coffee +58 -0
  89. data/test/javascripts/tests/helpers/url_test.js.coffee +76 -0
  90. data/test/javascripts/tests/underscore/underscore.outcasts.test.js.coffee +47 -0
  91. data/test/javascripts/vendors/backbone.js +1533 -0
  92. data/test/javascripts/vendors/i18n-lite.js.coffee +20 -0
  93. data/test/stylesheets/test_helper.css +4 -0
  94. data/test/stylesheets/vendors/ultimate/mixins/_routines.scss +120 -0
  95. data/test/stylesheets/vendors/ultimate/mixins/_vendors.scss +44 -0
  96. data/test/stylesheets/vendors/ultimate/mixins/css3/_text-shadow.scss +40 -0
  97. data/test/stylesheets/vendors/ultimate/mixins/css3.scss +350 -0
  98. data/test/stylesheets/vendors/ultimate/mixins/fonts.scss +100 -0
  99. data/test/stylesheets/vendors/ultimate/mixins/microstructures.scss +239 -0
  100. data/ultimate-helpers.gemspec +25 -0
  101. metadata +193 -0
@@ -0,0 +1,91 @@
1
+ #= require ./base
2
+
3
+ @Ultimate.Helpers.Tag =
4
+
5
+ BOOLEAN_ATTRIBUTES: "disabled readonly multiple checked autobuffer
6
+ autoplay controls loop selected hidden scoped async
7
+ defer reversed ismap seemless muted required
8
+ autofocus novalidate formnovalidate open pubdate itemscope".split(/\s+/)
9
+
10
+ PRE_CONTENT_STRINGS:
11
+ textarea: "\n"
12
+
13
+ tag: (tag_name, options = {}, open = false, escape = true) ->
14
+ "<#{tag_name}#{@tag_options(options, escape)}#{if open then '>' else ' />'}"
15
+
16
+ content_tag: (name, content_or_options_with_block = null, options = null, escape = true, block = null) ->
17
+ if block = _.outcasts.blockGiven(arguments)
18
+ options = content_or_options_with_block
19
+ @content_tag_string(name, block(), options, escape)
20
+ else
21
+ @content_tag_string(name, content_or_options_with_block, options, escape)
22
+
23
+ cdata_section: (content) ->
24
+ splitted = content.replace(/\]\]>/g, ']]]]><![CDATA[>')
25
+ "<![CDATA[#{splitted}]]>"
26
+
27
+ content_tag_string: (name, content = '', options = {}, escape = true, _escapeContent = escape) ->
28
+ content = _.string.escapeHTML(content) if _escapeContent
29
+ "<#{name}#{@tag_options(options, escape)}>#{@PRE_CONTENT_STRINGS[name] ? ''}#{content}</#{name}>"
30
+
31
+ html_options_to_s: (html_options, escape = false, prefix = "") ->
32
+ deprecate 'html_options_to_s()', "tag_options()"
33
+ @tag_options arguments...
34
+
35
+ tag_options: (options, escape = true) ->
36
+ return "" if _.isEmpty(options)
37
+ attrs = []
38
+ for key, value of options
39
+ if key is "data" and _.isObject(value)
40
+ for k, v of value
41
+ attrs.push @data_tag_option(k, v, escape)
42
+ else if _.include(@BOOLEAN_ATTRIBUTES, key)
43
+ attrs.push @boolean_tag_option(key) if value
44
+ else if value?
45
+ attrs.push @tag_option(key, value, escape)
46
+ if _.isEmpty(attrs) then "" else " #{attrs.sort().join(' ')}"
47
+
48
+ data_tag_option: (key, value, escape) ->
49
+ key = "data-#{_.string.dasherize(key)}"
50
+ value = JSON.stringify(value) if JSON? and _.isObject(value)
51
+ @tag_option(key, value, escape)
52
+
53
+ boolean_tag_option: (key) ->
54
+ "#{key}=\"#{key}\""
55
+
56
+ tag_option: (key, value, escape) ->
57
+ value = value.join(" ") if _.isArray(value)
58
+ value = _.string.escapeHTML(value) if escape
59
+ "#{key}=\"#{value}\""
60
+
61
+ concat_class: ->
62
+ flatten_classes = _.filter( _.flatten(arguments), (e) -> _.isString(e) ).join(' ')
63
+ _.uniq( _.string.words(flatten_classes) ).join(' ')
64
+
65
+
66
+
67
+ selfClosedTags: _.string.words('area base br col command embed hr img input keygen link meta param source track wbr')
68
+
69
+ # Generate html from zen-selector. Ninja tool.
70
+ # TODO more zen features: +, *x, {content}
71
+ # TODO cache
72
+ selectorToHtml: (selector) ->
73
+ if matches = selector.match(/^[\s>]*([\w\-\.#]+)(.*)$/)
74
+ selector = matches[1]
75
+ continuation = matches[2] # in v1 {(if continuation then ">" + content else " />")}
76
+ tag_name = selector.match(/^\w+/)?[0] or 'div'
77
+ id = selector.match(/#([\w\-]+)/)?[1]
78
+ classes = _.map( selector.match(/\.[\w\-]+/g), (c) -> _.string.ltrim(c, '.') )
79
+ html_options = {}
80
+ html_options['id'] = id if id
81
+ html_options['class'] = classes.join(' ') if classes.length
82
+ if _.contains(@selfClosedTags, tag_name)
83
+ @tag(tag_name, html_options)
84
+ else
85
+ continuation = @selectorToHtml(continuation) if continuation
86
+ @content_tag(tag_name, continuation, html_options)
87
+ # else if matches = selector.match(/^\s*\+\s*(.*)$/) # /^\s*\+\s*([\w\.#]+)(.*)$/)
88
+ # continuation = matches[1]
89
+ # if continuation then @selectorToHtml(continuation) else continuation
90
+ else
91
+ ''
@@ -0,0 +1,152 @@
1
+ #= require ./base
2
+ #= require ./tag
3
+ #= require ./javascript
4
+
5
+ __char_encode = (char) -> "%#{char.charCodeAt(0).toString(16)}"
6
+ __escape_path = (str) -> str.replace(/[^*\-.0-9A-Z_a-z]/g, __char_encode).replace(/\+/g, '%20')
7
+ __string_encode = (str) -> _.map(str, (char) -> "&##{char.charCodeAt(0)};" ).join('')
8
+
9
+ @Ultimate.Helpers.Url =
10
+
11
+ url_for: (options = null) ->
12
+ if _.isString(options)
13
+ if options is 'back'
14
+ 'javascript:history.back();'
15
+ else
16
+ options
17
+ else unless _.isEmpty(options)
18
+ url = _.result(options, 'url') ? ''
19
+ if $.isPlainObject(options)
20
+ options = _.clone(options)
21
+ delete options['url']
22
+ anchor = _.outcasts.delete(options, 'anchor')
23
+ url += "?#{_.map(options, (value, key) -> "#{key}=#{value}").sort().join('&')}" unless _.isEmpty(options)
24
+ url += "##{anchor}" if anchor
25
+ url
26
+ else
27
+ 'javascript:;'
28
+
29
+ link_to: (name = null, options = null, html_options = null, block = null) ->
30
+ [html_options, options] = [options, name] if block = _.outcasts.blockGiven(arguments)
31
+ options ||= {}
32
+ url = @url_for(options)
33
+ html_options = @_convert_options_to_data_attributes(options, html_options)
34
+ html_options['href'] ||= url
35
+ if block
36
+ Ultimate.Helpers.Tag.content_tag('a', html_options, null, false, block)
37
+ else
38
+ Ultimate.Helpers.Tag.content_tag('a', name ? url, html_options, false)
39
+
40
+ link_to_js: (name = null, html_options = null, block = null) ->
41
+ [options, name] = [name, null] if block = _.outcasts.blockGiven(arguments)
42
+ @link_to [name, options, html_options, block]...
43
+
44
+ # TODO tests
45
+ link_to_unless_current_span: (name, options = null, html_options = null, block = null) ->
46
+ [html_options, options] = [options, name] if block = _.outcasts.blockGiven(arguments)
47
+ url = @url_for(options)
48
+ if @current_page_(url)
49
+ if block
50
+ Ultimate.Helpers.Tag.content_tag('span', html_options, null, false, block)
51
+ else
52
+ Ultimate.Helpers.Tag.content_tag('span', name ? url, html_options, false)
53
+ else
54
+ if block
55
+ @link_to options, html_options, block
56
+ else
57
+ @link_to name, options, html_options
58
+
59
+ # TODO tests
60
+ link_to_unless_current: (name, options = {}, html_options = {}, block = null) ->
61
+ @link_to_unless @current_page_(options), name, options, html_options, block
62
+
63
+ # TODO tests
64
+ link_to_unless: (condition, name, options = {}, html_options = {}, block = null) ->
65
+ if condition
66
+ if block = _.outcasts.blockGiven(arguments)
67
+ block(name, options, html_options)
68
+ else
69
+ name
70
+ else
71
+ @link_to name, options, html_options, block
72
+
73
+ # TODO tests
74
+ link_to_if: (condition, name, options = {}, html_options = {}, block = null) ->
75
+ @link_to_unless not condition, name, options, html_options, block
76
+
77
+ mail_to: (email_address, name = null, html_options = {}) ->
78
+ email_address = _.string.escapeHTML(email_address)
79
+ encode = _.outcasts.delete(html_options, 'encode')
80
+ extras = _.compact _.map _.string.words('cc bcc body subject'), (item) ->
81
+ option = _.outcasts.delete(html_options, item)
82
+ if option?
83
+ "#{item}=#{__escape_path(option)}"
84
+ extras = if _.isEmpty(extras) then '' else '?' + _.string.escapeHTML(extras.join('&'))
85
+ email_address_obfuscated = email_address
86
+ email_address_obfuscated = email_address_obfuscated.replace('@', _.outcasts.delete(html_options, 'replace_at')) if 'replace_at' of html_options
87
+ email_address_obfuscated = email_address_obfuscated.replace('.', _.outcasts.delete(html_options, 'replace_dot')) if 'replace_dot' of html_options
88
+ switch encode
89
+ when 'javascript'
90
+ html = @link_to(name or email_address_obfuscated, "mailto:#{email_address}#{extras}", html_options)
91
+ html = Ultimate.Helpers.Javascript.escape_javascript(html)
92
+ string = _.map("document.write('#{html}');", __char_encode).join('')
93
+ "<script>eval(decodeURIComponent('#{string}'))</script>"
94
+ when 'hex'
95
+ email_address_encoded = __string_encode(email_address_obfuscated)
96
+ string = __string_encode('mailto:') + _.map(email_address, (char) -> if /\w/.test(char) then __char_encode(char) else char).join('')
97
+ @link_to name or email_address_encoded, "#{string}#{extras}", html_options
98
+ else
99
+ @link_to name or email_address_obfuscated, "mailto:#{email_address}#{extras}", html_options
100
+
101
+ # TODO tests
102
+ current_page_: (options) ->
103
+ url_string = @url_for(options)
104
+ request_uri = location.pathname
105
+ if /^\w+:\/\//.test(url_string)
106
+ url_string == "#{location.protocol}#{location.host}#{request_uri}"
107
+ else
108
+ url_string == request_uri
109
+
110
+
111
+
112
+ _convert_options_to_data_attributes: (options, html_options) ->
113
+ if html_options
114
+ html_options['data-remote'] = 'true' if @_link_to_remote_options(options) or @_link_to_remote_options(html_options)
115
+ method = _.outcasts.delete(html_options, 'method')
116
+ @_add_method_to_attributes(html_options, method) if method
117
+ html_options
118
+ else
119
+ if @_link_to_remote_options(options) then {'data-remote': 'true'} else {}
120
+
121
+ _link_to_remote_options: (options) ->
122
+ _.isObject(options) and _.outcasts.delete(options, 'remote')
123
+
124
+ _add_method_to_attributes: (html_options, method) ->
125
+ if _.isString(method) and method.toLowerCase() isnt 'get' and not /nofollow/.test(html_options['rel'])
126
+ html_options['rel'] = Ultimate.Helpers.Tag.concat_class(html_options['rel'], 'nofollow')
127
+ html_options['data-method'] = method
128
+
129
+ _convert_boolean_attributes: (html_options, bool_attrs) ->
130
+ html_options[x] = x for x in bool_attrs when _.outcasts.delete(html_options, x)
131
+ html_options
132
+
133
+ __protect_against_forgery: false
134
+ __request_forgery_protection_token: 'form_token'
135
+ __form_authenticity_token: 'secret'
136
+
137
+ __init_request_forgery_protection: ->
138
+ param = $('head meta[name="csrf-param"]').attr('content')
139
+ token = $('head meta[name="csrf-token"]').attr('content')
140
+ if param and token
141
+ @__protect_against_forgery = true
142
+ @__request_forgery_protection_token = param
143
+ @__form_authenticity_token = token
144
+
145
+ _token_tag: (token = @__form_authenticity_token) ->
146
+ if token isnt false and @__protect_against_forgery
147
+ Ultimate.Helpers.Tag.tag 'input', type: 'hidden', name: @__request_forgery_protection_token, value: token
148
+ else
149
+ ''
150
+
151
+ _method_tag: (method) ->
152
+ Ultimate.Helpers.Tag.tag 'input', type: 'hidden', name: '_method', value: method
@@ -0,0 +1,122 @@
1
+ ###
2
+ * front-end js helpers
3
+ * 0.3.3.alpha / 2010-2011
4
+ * Karpunin Dmitry / Evrone.com
5
+ * koderfunk_at_gmail_dot_com
6
+ ###
7
+
8
+ @DEBUG_MODE ?= false
9
+ @TEST_MODE ?= false
10
+ @LOG_TODO ?= true
11
+
12
+ @cout = =>
13
+ args = _.toArray(arguments)
14
+ method = if args[0] in ['log', 'info', 'warn', 'error', 'assert', 'clear'] then args.shift() else 'log'
15
+ if @DEBUG_MODE and console?
16
+ method = console[method]
17
+ if method.apply?
18
+ method.apply(console, args)
19
+ else
20
+ method(args)
21
+ return args[0]
22
+
23
+ @_cout = =>
24
+ console.log(arguments) if console?
25
+ arguments[0]
26
+
27
+ @deprecate = (subject, instead = null) =>
28
+ @cout 'error', "`#{subject}` DEPRECATED!#{if instead then " Use instead `#{instead}`" else ''}"
29
+
30
+ @todo = (subject, location = null, numberOrString = null) =>
31
+ if @LOG_TODO
32
+ @cout 'warn', "TODO: #{subject}#{if location then " ### #{location}" else ''}#{if numberOrString then (if _.isNumber(numberOrString) then ":#{numberOrString}" else " > #{numberOrString}") else ''}"
33
+
34
+ @args = (a) ->
35
+ @deprecate 'arg()', '_.toArray()'
36
+ r = []
37
+ Array::push.apply(r, a) if a.length > 0
38
+ r
39
+
40
+ @logicalXOR = (a, b) ->
41
+ ( a and not b ) or ( not a and b )
42
+
43
+ @bound = (number, min, max) ->
44
+ Math.max(min, Math.min(max, number))
45
+
46
+
47
+
48
+ @getParams = ->
49
+ q = location.search.substring(1).split('&')
50
+ r = {}
51
+ for e in q
52
+ t = e.split('=')
53
+ r[t[0]] = t[1]
54
+ r
55
+
56
+ @respondFormat = (url, format = null) ->
57
+ aq = url.split('?')
58
+ ah = aq[0].split('#')
59
+ ad = ah[0].split('.')
60
+ currentFormat = if ad.length > 1 and not /\//.test(ad[ad.length - 1]) then ad.pop() else ''
61
+ return currentFormat unless format?
62
+ return url if format is currentFormat
63
+ ad.push format if format
64
+ ah[0] = ad.join('.')
65
+ aq[0] = ah.join('#')
66
+ aq.join('?')
67
+
68
+
69
+
70
+ ########### Deprecated ###########
71
+
72
+ # Helper function to get a value from a object as a property or as a function.
73
+ @getValue = (object, prop) ->
74
+ @deprecate "getValue()", "_.result()"
75
+ return null unless object and object[prop]
76
+ return if _.isFunction(object[prop]) then object[prop]() else object[prop]
77
+
78
+ @isset = (obj) =>
79
+ @deprecate 'isset(obj)', '_.isUndefined(obj) OR "obj isnt undefined" OR "obj?'
80
+ obj isnt undefined
81
+
82
+ @isString = (v) =>
83
+ @deprecate 'isString(v)', '_.isString(v)'
84
+ _.isString v
85
+
86
+ @isNumber = (v) =>
87
+ @deprecate 'isNumber(v)', '_.isNumber(v)'
88
+ not isNaN(parseInt(v))
89
+
90
+ @isJQ = (obj) ->
91
+ @deprecate 'isJQ(obj)', 'obj instanceof jQuery'
92
+ _.isObject(obj) and _.isString(obj.jquery)
93
+
94
+ @uniq = (arrOrString) ->
95
+ @deprecate 'uniq(a)', '_.uniq(a)'
96
+ isStr = _.isString(arrOrString)
97
+ return arrOrString unless isStr or _.isArray(arrOrString)
98
+ r = []
99
+ r.push(e) for e in arrOrString when not _.include(r, e)
100
+ if isStr then r.join('') else r
101
+
102
+ @regexpSpace = /^\s*$/
103
+ @regexpTrim = /^\s*(.*?)\s*$/
104
+
105
+ @strTrim = (s) =>
106
+ @deprecate "strTrim(s)", "_.trim(s)"
107
+ s.match(@regexpTrim)[1]
108
+
109
+
110
+
111
+ @rails_data = {}
112
+
113
+ @rails_scope = (controller_name, action_name, scopedCloasure = null, scopedCloasureArguments...) =>
114
+ @deprecate 'rails_scope'
115
+ return false if _.isString(controller_name) and controller_name isnt @rails_data['controller_name']
116
+ return false if _.isString(action_name) and action_name isnt @rails_data['action_name']
117
+ if _.isFunction(scopedCloasure)
118
+ arguments[2] scopedCloasureArguments...
119
+ true
120
+
121
+ $ =>
122
+ @rails_data = $('body').data()
@@ -0,0 +1,85 @@
1
+ # TODO: register components
2
+
3
+ do ($ = jQuery) =>
4
+
5
+ $.regexp ||= {}
6
+
7
+ $.regexp.rorId ||= /(\w+)_(\d+)$/
8
+
9
+ @ror_id = (jObjOrString) ->
10
+ if jObjOrString instanceof jQuery
11
+ id = jObjOrString.data("rorId") # Maybe use "id"
12
+ unless id?
13
+ id = ror_id(jObjOrString.attr("id"))
14
+ jObjOrString.data("rorId", id) if id?
15
+ id
16
+ else if _.isString(jObjOrString)
17
+ matchResult = jObjOrString.match($.regexp.rorId)
18
+ if matchResult then matchResult[2] else null
19
+ else
20
+ null
21
+
22
+ $.fn.rorId = (id = null, prefix = null) ->
23
+ if arguments.length
24
+ jThis = @first()
25
+ thisId = @attr("id")
26
+ if not prefix? and _.isString(thisId)
27
+ prefix = matchResult[1] if matchResult = thisId.match($.regexp.rorId)
28
+ @data("rorId", id)
29
+ if prefix?
30
+ jThis.attr "id", "#{prefix}_#{id}"
31
+ jThis
32
+ else
33
+ ror_id @
34
+
35
+ $.fn.getClasses = ->
36
+ _.string.words @attr("class")
37
+
38
+ # Get hash of html-dom attributes from first matched element or false, if no elements
39
+ $.fn.getAttributes = ->
40
+ attrs = {}
41
+ if @length
42
+ attrs[attr.nodeName] = attr.nodeValue for attr in this[0].attributes
43
+ attrs
44
+
45
+ # [showOrHide[, duration[, callback]]]
46
+ $.fn.slideToggleByState = ->
47
+ if @length
48
+ if arguments.length > 0
49
+ a = _.toArray(arguments)
50
+ if a.shift()
51
+ @slideDown.apply @, a
52
+ else
53
+ @slideUp.apply @, a
54
+ else
55
+ @slideToggle()
56
+ @
57
+
58
+ unless $.isHTML
59
+ $.regexp.HTML = /^\s*<(\w+)[\S\s]*<\/(\w+)>\s*$/
60
+ $.isHTML = (content, strong = false) ->
61
+ return false unless _.isString(content)
62
+ matches = content.match($.regexp.HTML)
63
+ return false unless matches?
64
+ not strong or matches[1] is matches[2]
65
+
66
+ # TODO replace usages to underscore methods and using distribution
67
+ unless $.isRegExp
68
+ $.isRegExp = (candidate) ->
69
+ deprecate '$.isRegExp', '_.isRegExp'
70
+ typeof candidate is "object" and typeof candidate.test is "function"
71
+
72
+ unless $.isBoolean
73
+ $.isBoolean = (v) ->
74
+ deprecate '$.isBoolean', '_.isBoolean'
75
+ typeof v is "boolean"
76
+
77
+ unless $.isString
78
+ $.isString = (v) ->
79
+ deprecate '$.isString', '_.isString'
80
+ typeof v is "string"
81
+
82
+ if typeof $.isEmptyString is "undefined"
83
+ $.isEmptyString = (v) ->
84
+ deprecate '$.isEmptyString', '_.isBlank'
85
+ regexpSpace.test v
@@ -0,0 +1,176 @@
1
+ // Underscore.inflection.js
2
+ // (c) 2011 Jeremy Ruppel
3
+ // Underscore.inflection is freely distributable under the MIT license.
4
+ // Portions of Underscore.inflection are inspired or borrowed from ActiveSupport
5
+ // Version 1.0.0
6
+
7
+ ( function( _, undefined )
8
+ {
9
+ var
10
+ plurals = [ ],
11
+
12
+ singulars = [ ],
13
+
14
+ uncountables = [ ];
15
+
16
+ /**
17
+ * Inflector
18
+ */
19
+ var inflector = {
20
+
21
+ gsub : function( word, rule, replacement )
22
+ {
23
+ var pattern = new RegExp( rule.source || rule, 'gi' );
24
+
25
+ return pattern.test( word ) ? word.replace( pattern, replacement ) : null;
26
+ },
27
+
28
+ plural : function( rule, replacement )
29
+ {
30
+ plurals.unshift( [ rule, replacement ] );
31
+ },
32
+
33
+ pluralize : function( word, count, includeNumber )
34
+ {
35
+ var result;
36
+
37
+ if( count !== undefined )
38
+ {
39
+ result = ( count === 1 ) ? this.singularize( word ) : this.pluralize( word );
40
+ result = ( includeNumber ) ? [ count, result ].join( ' ' ) : result;
41
+ }
42
+ else
43
+ {
44
+ if( _( uncountables ).include( word ) )
45
+ {
46
+ return word;
47
+ }
48
+
49
+ result = word;
50
+
51
+ _( plurals ).detect( function( rule )
52
+ {
53
+ var gsub = this.gsub( word, rule[ 0 ], rule[ 1 ] );
54
+
55
+ return gsub ? ( result = gsub ) : false;
56
+ },
57
+ this );
58
+ }
59
+
60
+ return result;
61
+ },
62
+
63
+ singular : function( rule, replacement )
64
+ {
65
+ singulars.unshift( [ rule, replacement ] );
66
+ },
67
+
68
+ singularize : function( word )
69
+ {
70
+ if( _( uncountables ).include( word ) )
71
+ {
72
+ return word;
73
+ }
74
+
75
+ var result = word;
76
+
77
+ _( singulars ).detect( function( rule )
78
+ {
79
+ var gsub = this.gsub( word, rule[ 0 ], rule[ 1 ] );
80
+
81
+ return gsub ? ( result = gsub ) : false;
82
+ },
83
+ this );
84
+
85
+ return result;
86
+ },
87
+
88
+ irregular : function( singular, plural )
89
+ {
90
+ this.plural( '\\b' + singular + '\\b', plural );
91
+ this.singular( '\\b' + plural + '\\b', singular );
92
+ },
93
+
94
+ uncountable : function( word )
95
+ {
96
+ uncountables.unshift( word );
97
+ },
98
+
99
+ resetInflections : function( )
100
+ {
101
+ plurals = [ ];
102
+ singulars = [ ];
103
+ uncountables = [ ];
104
+
105
+ this.plural( /$/, 's' );
106
+ this.plural( /s$/, 's' );
107
+ this.plural( /(ax|test)is$/, '$1es' );
108
+ this.plural( /(octop|vir)us$/, '$1i' );
109
+ this.plural( /(octop|vir)i$/, '$1i' );
110
+ this.plural( /(alias|status)$/, '$1es' );
111
+ this.plural( /(bu)s$/, '$1ses' );
112
+ this.plural( /(buffal|tomat)o$/, '$1oes' );
113
+ this.plural( /([ti])um$/, '$1a' );
114
+ this.plural( /([ti])a$/, '$1a' );
115
+ this.plural( /sis$/, 'ses' );
116
+ this.plural( /(?:([^f])fe|([lr])f)$/, '$1$2ves' );
117
+ this.plural( /(hive)$/, '$1s' );
118
+ this.plural( /([^aeiouy]|qu)y$/, '$1ies' );
119
+ this.plural( /(x|ch|ss|sh)$/, '$1es' );
120
+ this.plural( /(matr|vert|ind)(?:ix|ex)$/, '$1ices' );
121
+ this.plural( /([m|l])ouse$/, '$1ice' );
122
+ this.plural( /([m|l])ice$/, '$1ice' );
123
+ this.plural( /^(ox)$/, '$1en' );
124
+ this.plural( /^(oxen)$/, '$1' );
125
+ this.plural( /(quiz)$/, '$1zes' );
126
+
127
+ this.singular( /s$/, '' );
128
+ this.singular( /(n)ews$/, '$1ews' );
129
+ this.singular( /([ti])a$/, '$1um' );
130
+ this.singular( /((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/, '$1$2sis' );
131
+ this.singular( /(^analy)ses$/, '$1sis' );
132
+ this.singular( /([^f])ves$/, '$1fe' );
133
+ this.singular( /(hive)s$/, '$1' );
134
+ this.singular( /(tive)s$/, '$1' );
135
+ this.singular( /([lr])ves$/, '$1f' );
136
+ this.singular( /([^aeiouy]|qu)ies$/, '$1y' );
137
+ this.singular( /(s)eries$/, '$1eries' );
138
+ this.singular( /(m)ovies$/, '$1ovie' );
139
+ this.singular( /(x|ch|ss|sh)es$/, '$1' );
140
+ this.singular( /([m|l])ice$/, '$1ouse' );
141
+ this.singular( /(bus)es$/, '$1' );
142
+ this.singular( /(o)es$/, '$1' );
143
+ this.singular( /(shoe)s$/, '$1' );
144
+ this.singular( /(cris|ax|test)es$/, '$1is' );
145
+ this.singular( /(octop|vir)i$/, '$1us' );
146
+ this.singular( /(alias|status)es$/, '$1' );
147
+ this.singular( /^(ox)en/, '$1' );
148
+ this.singular( /(vert|ind)ices$/, '$1ex' );
149
+ this.singular( /(matr)ices$/, '$1ix' );
150
+ this.singular( /(quiz)zes$/, '$1' );
151
+ this.singular( /(database)s$/, '$1' );
152
+
153
+ this.irregular( 'person', 'people' );
154
+ this.irregular( 'man', 'men' );
155
+ this.irregular( 'child', 'children' );
156
+ this.irregular( 'sex', 'sexes' );
157
+ this.irregular( 'move', 'moves' );
158
+ this.irregular( 'cow', 'kine' );
159
+
160
+ _( 'equipment information rice money species series fish sheep jeans'.split( /\s+/ ) ).each( function( word )
161
+ {
162
+ this.uncountable( word );
163
+ },
164
+ this );
165
+
166
+ return this;
167
+ }
168
+
169
+ };
170
+
171
+ /**
172
+ * Underscore integration
173
+ */
174
+ _.mixin( inflector.resetInflections( ) );
175
+
176
+ } )( _ );