ultimate-base 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rvmrc +2 -0
- data/.rvmrc.example +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -0
- data/LICENSE +5 -0
- data/README.md +1 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/backbone/backbone.js +1452 -0
- data/app/assets/javascripts/backbone/ultimate/app.js.coffee +25 -0
- data/app/assets/javascripts/backbone/ultimate/collection.js.coffee +12 -0
- data/app/assets/javascripts/backbone/ultimate/model.js.coffee +12 -0
- data/app/assets/javascripts/backbone/ultimate/router.js.coffee +13 -0
- data/app/assets/javascripts/backbone/ultimate/view.js.coffee +96 -0
- data/app/assets/javascripts/ultimate/base.js.coffee +103 -0
- data/app/assets/javascripts/ultimate/bus.js.coffee +57 -0
- data/app/assets/javascripts/ultimate/devise.js.coffee +18 -0
- data/app/assets/javascripts/ultimate/experimental/_inflections/dzone.inflections.js +154 -0
- data/app/assets/javascripts/ultimate/experimental/_inflections/inflections.js.coffee +2 -0
- data/app/assets/javascripts/ultimate/experimental/_inflections/plur.js +29 -0
- data/app/assets/javascripts/ultimate/experimental/_inflections/underscore.inflection.js +175 -0
- data/app/assets/javascripts/ultimate/experimental/fuzzy-json-generator.js.coffee +48 -0
- data/app/assets/javascripts/ultimate/helpers/array.js.coffee +63 -0
- data/app/assets/javascripts/ultimate/helpers/asset_tag.js.coffee +63 -0
- data/app/assets/javascripts/ultimate/helpers/decor.js.coffee +14 -0
- data/app/assets/javascripts/ultimate/helpers/forms.js.coffee +0 -0
- data/app/assets/javascripts/ultimate/helpers/tags.js.coffee +70 -0
- data/app/assets/javascripts/ultimate/helpers.js.coffee +149 -0
- data/app/assets/javascripts/ultimate/improves/datepicker.js.coffee +34 -0
- data/app/assets/javascripts/ultimate/improves/form-errors.js.coffee +146 -0
- data/app/assets/javascripts/ultimate/improves/form.js.coffee +155 -0
- data/app/assets/javascripts/ultimate/improves/magic-radios.js.coffee +41 -0
- data/app/assets/javascripts/ultimate/improves/tablesorter.js +59 -0
- data/app/assets/javascripts/ultimate/improves/typed-field.js.coffee +98 -0
- data/app/assets/javascripts/ultimate/underscore/underscore.js +1059 -0
- data/app/assets/javascripts/ultimate/underscore/underscore.string.js +480 -0
- data/app/assets/javascripts/ultimate/widgets/dock.js.coffee +70 -0
- data/app/assets/javascripts/ultimate/widgets/gear.js.coffee +84 -0
- data/app/assets/javascripts/ultimate/widgets/jquery-ext.js.coffee +104 -0
- data/app/assets/javascripts/ultimate/widgets/jquery.adapter.js.coffee +62 -0
- data/app/assets/javascripts/ultimate/widgets/widget.js.coffee +115 -0
- data/app/assets/stylesheets/polyfills/PIE.htc +96 -0
- data/app/assets/stylesheets/polyfills/boxsizing.htc +300 -0
- data/app/assets/stylesheets/ultimate/mixins/_routines.css.scss +95 -0
- data/app/assets/stylesheets/ultimate/mixins/_vendors.css.scss +34 -0
- data/app/assets/stylesheets/ultimate/mixins/css3/_text-shadow.scss +37 -0
- data/app/assets/stylesheets/ultimate/mixins/css3.css.scss +328 -0
- data/app/assets/stylesheets/ultimate/mixins/decor.css.scss +86 -0
- data/app/assets/stylesheets/ultimate/mixins/fonts.css.scss +100 -0
- data/app/assets/stylesheets/ultimate/mixins/microstructures.css.scss +188 -0
- data/app/assets/stylesheets/ultimate/structures/slider.css.scss +53 -0
- data/lib/ultimate-base/engine.rb +6 -0
- data/lib/ultimate-base/extensions/directive_processor.rb +64 -0
- data/lib/ultimate-base/extensions/sass_script_functions.rb +39 -0
- data/lib/ultimate-base/version.rb +5 -0
- data/lib/ultimate-base.rb +10 -0
- data/ultimate-base.gemspec +25 -0
- metadata +102 -0
@@ -0,0 +1,175 @@
|
|
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
|
+
/**
|
10
|
+
* Inflector
|
11
|
+
*/
|
12
|
+
var inflector = {
|
13
|
+
|
14
|
+
plurals : [ ],
|
15
|
+
|
16
|
+
singulars : [ ],
|
17
|
+
|
18
|
+
uncountables : [ ],
|
19
|
+
|
20
|
+
gsub : function( word, rule, replacement )
|
21
|
+
{
|
22
|
+
var pattern = new RegExp( rule.source || rule, 'gi' );
|
23
|
+
|
24
|
+
return pattern.test( word ) ? word.replace( pattern, replacement ) : null;
|
25
|
+
},
|
26
|
+
|
27
|
+
plural : function( rule, replacement )
|
28
|
+
{
|
29
|
+
this.plurals.unshift( [ rule, replacement ] );
|
30
|
+
},
|
31
|
+
|
32
|
+
pluralize : function( word, count, includeNumber )
|
33
|
+
{
|
34
|
+
var result;
|
35
|
+
|
36
|
+
if( count !== undefined )
|
37
|
+
{
|
38
|
+
result = ( count === 1 ) ? this.singularize( word ) : this.pluralize( word );
|
39
|
+
result = ( includeNumber ) ? [ count, result ].join( ' ' ) : result;
|
40
|
+
}
|
41
|
+
else
|
42
|
+
{
|
43
|
+
if( _( this.uncountables ).include( word ) )
|
44
|
+
{
|
45
|
+
return word;
|
46
|
+
}
|
47
|
+
|
48
|
+
result = word;
|
49
|
+
|
50
|
+
_( this.plurals ).detect( function( rule )
|
51
|
+
{
|
52
|
+
var gsub = this.gsub( word, rule[ 0 ], rule[ 1 ] );
|
53
|
+
|
54
|
+
return gsub ? ( result = gsub ) : false;
|
55
|
+
},
|
56
|
+
this );
|
57
|
+
}
|
58
|
+
|
59
|
+
return result;
|
60
|
+
},
|
61
|
+
|
62
|
+
singular : function( rule, replacement )
|
63
|
+
{
|
64
|
+
this.singulars.unshift( [ rule, replacement ] );
|
65
|
+
},
|
66
|
+
|
67
|
+
singularize : function( word )
|
68
|
+
{
|
69
|
+
if( _( this.uncountables ).include( word ) )
|
70
|
+
{
|
71
|
+
return word;
|
72
|
+
}
|
73
|
+
|
74
|
+
var result = word;
|
75
|
+
|
76
|
+
_( this.singulars ).detect( function( rule )
|
77
|
+
{
|
78
|
+
var gsub = this.gsub( word, rule[ 0 ], rule[ 1 ] );
|
79
|
+
|
80
|
+
return gsub ? ( result = gsub ) : false;
|
81
|
+
},
|
82
|
+
this );
|
83
|
+
|
84
|
+
return result;
|
85
|
+
},
|
86
|
+
|
87
|
+
irregular : function( singular, plural )
|
88
|
+
{
|
89
|
+
this.plural( singular, plural );
|
90
|
+
this.singular( plural, singular );
|
91
|
+
},
|
92
|
+
|
93
|
+
uncountable : function( word )
|
94
|
+
{
|
95
|
+
this.uncountables.unshift( word );
|
96
|
+
},
|
97
|
+
|
98
|
+
resetInflections : function( )
|
99
|
+
{
|
100
|
+
this.plurals = [ ];
|
101
|
+
this.singulars = [ ];
|
102
|
+
this.uncountables = [ ];
|
103
|
+
|
104
|
+
this.plural( /$/, 's' );
|
105
|
+
this.plural( /s$/, 's' );
|
106
|
+
this.plural( /(ax|test)is$/, '$1es' );
|
107
|
+
this.plural( /(octop|vir)us$/, '$1i' );
|
108
|
+
this.plural( /(octop|vir)i$/, '$1i' );
|
109
|
+
this.plural( /(alias|status)$/, '$1es' );
|
110
|
+
this.plural( /(bu)s$/, '$1ses' );
|
111
|
+
this.plural( /(buffal|tomat)o$/, '$1oes' );
|
112
|
+
this.plural( /([ti])um$/, '$1a' );
|
113
|
+
this.plural( /([ti])a$/, '$1a' );
|
114
|
+
this.plural( /sis$/, 'ses' );
|
115
|
+
this.plural( /(?:([^f])fe|([lr])f)$/, '$1$2ves' );
|
116
|
+
this.plural( /(hive)$/, '$1s' );
|
117
|
+
this.plural( /([^aeiouy]|qu)y$/, '$1ies' );
|
118
|
+
this.plural( /(x|ch|ss|sh)$/, '$1es' );
|
119
|
+
this.plural( /(matr|vert|ind)(?:ix|ex)$/, '$1ices' );
|
120
|
+
this.plural( /([m|l])ouse$/, '$1ice' );
|
121
|
+
this.plural( /([m|l])ice$/, '$1ice' );
|
122
|
+
this.plural( /^(ox)$/, '$1en' );
|
123
|
+
this.plural( /^(oxen)$/, '$1' );
|
124
|
+
this.plural( /(quiz)$/, '$1zes' );
|
125
|
+
|
126
|
+
this.singular( /s$/, '' );
|
127
|
+
this.singular( /(n)ews$/, '$1ews' );
|
128
|
+
this.singular( /([ti])a$/, '$1um' );
|
129
|
+
this.singular( /((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/, '$1$2sis' );
|
130
|
+
this.singular( /(^analy)ses$/, '$1sis' );
|
131
|
+
this.singular( /([^f])ves$/, '$1fe' );
|
132
|
+
this.singular( /(hive)s$/, '$1' );
|
133
|
+
this.singular( /(tive)s$/, '$1' );
|
134
|
+
this.singular( /([lr])ves$/, '$1f' );
|
135
|
+
this.singular( /([^aeiouy]|qu)ies$/, '$1y' );
|
136
|
+
this.singular( /(s)eries$/, '$1eries' );
|
137
|
+
this.singular( /(m)ovies$/, '$1ovie' );
|
138
|
+
this.singular( /(x|ch|ss|sh)es$/, '$1' );
|
139
|
+
this.singular( /([m|l])ice$/, '$1ouse' );
|
140
|
+
this.singular( /(bus)es$/, '$1' );
|
141
|
+
this.singular( /(o)es$/, '$1' );
|
142
|
+
this.singular( /(shoe)s$/, '$1' );
|
143
|
+
this.singular( /(cris|ax|test)es$/, '$1is' );
|
144
|
+
this.singular( /(octop|vir)i$/, '$1us' );
|
145
|
+
this.singular( /(alias|status)es$/, '$1' );
|
146
|
+
this.singular( /^(ox)en/, '$1' );
|
147
|
+
this.singular( /(vert|ind)ices$/, '$1ex' );
|
148
|
+
this.singular( /(matr)ices$/, '$1ix' );
|
149
|
+
this.singular( /(quiz)zes$/, '$1' );
|
150
|
+
this.singular( /(database)s$/, '$1' );
|
151
|
+
|
152
|
+
this.irregular( 'person', 'people' );
|
153
|
+
this.irregular( 'man', 'men' );
|
154
|
+
this.irregular( 'child', 'children' );
|
155
|
+
this.irregular( 'sex', 'sexes' );
|
156
|
+
this.irregular( 'move', 'moves' );
|
157
|
+
this.irregular( 'cow', 'kine' );
|
158
|
+
|
159
|
+
_( 'equipment information rice money species series fish sheep jeans'.split( /\s+/ ) ).each( function( word )
|
160
|
+
{
|
161
|
+
this.uncountable( word );
|
162
|
+
},
|
163
|
+
this );
|
164
|
+
|
165
|
+
return this;
|
166
|
+
}
|
167
|
+
|
168
|
+
};
|
169
|
+
|
170
|
+
/**
|
171
|
+
* Underscore integration
|
172
|
+
*/
|
173
|
+
_.mixin( inflector.resetInflections( ) );
|
174
|
+
|
175
|
+
} )( _ );
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class FuzzyJsonGenerator
|
2
|
+
|
3
|
+
@idCounter: 0
|
4
|
+
|
5
|
+
constructor: ->
|
6
|
+
# nop
|
7
|
+
|
8
|
+
nextId: ->
|
9
|
+
@constructor.idCounter++
|
10
|
+
|
11
|
+
# overload this method
|
12
|
+
generateOne: ->
|
13
|
+
id: @nextId()
|
14
|
+
|
15
|
+
generateMany: (count, base = 0) ->
|
16
|
+
count = @getRandom(count, base) if base > 0
|
17
|
+
@generateOne() for i in [0..count]
|
18
|
+
|
19
|
+
getRandom: (arrayOrCount, base = 0) ->
|
20
|
+
rand = Math.random()
|
21
|
+
if _.isArray arrayOrCount
|
22
|
+
arrayOrCount[Math.floor rand * arrayOrCount.length]
|
23
|
+
else if _.isNumber arrayOrCount
|
24
|
+
base + Math.floor rand * arrayOrCount
|
25
|
+
else
|
26
|
+
rand
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
@Generators ||= {}
|
31
|
+
|
32
|
+
class Generators.Offers extends FuzzyJsonGenerator
|
33
|
+
|
34
|
+
citiesFrom: ['Москва', 'Воронеж', 'Санкт-Петербург']
|
35
|
+
citiesTo: ['Хошимин', 'Брюссель', 'Лондон', 'Нью-Йорк', 'Париж', 'Мадрид', 'Берлин']
|
36
|
+
|
37
|
+
generateOne: ->
|
38
|
+
fromDate = new Date
|
39
|
+
fromDate.setTime(fromDate.getTime() + 1000 * 60 * 60 * 24 * @getRandom(30))
|
40
|
+
toDate = new Date
|
41
|
+
toDate.setTime(fromDate.getTime() + 1000 * 60 * 60 * 24 * @getRandom(30))
|
42
|
+
id: @nextId()
|
43
|
+
city_from : @getRandom(@citiesFrom)
|
44
|
+
city_to : @getRandom(@citiesTo)
|
45
|
+
date_from : Globalize.format(fromDate, "yyyy-MM-dd")
|
46
|
+
date_to : Globalize.format(toDate, "yyyy-MM-dd")
|
47
|
+
price : @getRandom(20000, 5000)
|
48
|
+
hot : @getRandom(2) is 1
|
@@ -0,0 +1,63 @@
|
|
1
|
+
@compact = (arr) ->
|
2
|
+
@deprecate 'compact(arr)', '_.compact arr'
|
3
|
+
r = []
|
4
|
+
for e in arr
|
5
|
+
r.push e if e?
|
6
|
+
r
|
7
|
+
|
8
|
+
@reverse = (arr) =>
|
9
|
+
@deprecate 'reverse(arr)', 'arr.reverse()'
|
10
|
+
_isString = _.isString arr
|
11
|
+
return arr unless _isString or $.isArray arr
|
12
|
+
if _isString then @warning 'reverse(someString)', 'STOP USE IT!' else arr.reverse()
|
13
|
+
|
14
|
+
###
|
15
|
+
Split array into slices of <number> elements.
|
16
|
+
Map result by iterator if last given.
|
17
|
+
|
18
|
+
>>> eachSlice [1..10], 3, (a) -> cout a
|
19
|
+
[1, 2, 3]
|
20
|
+
[4, 5, 6]
|
21
|
+
[7, 8, 9]
|
22
|
+
[10]
|
23
|
+
###
|
24
|
+
@eachSlice = (array, number, iterator = null) =>
|
25
|
+
size = array.length
|
26
|
+
index = 0
|
27
|
+
slices = []
|
28
|
+
while index < size
|
29
|
+
nextIndex = index + number
|
30
|
+
slices.push array.slice(index, nextIndex)
|
31
|
+
index = nextIndex
|
32
|
+
if _.isFunction iterator then _.map slices, iterator else slices
|
33
|
+
|
34
|
+
###
|
35
|
+
Splits or iterates over the array in groups of size +number+,
|
36
|
+
padding any remaining slots with +fill_with+ unless it is +false+.
|
37
|
+
|
38
|
+
>>> inGroupsOf [1..7], 3, (group) -> cout group
|
39
|
+
[1, 2, 3]
|
40
|
+
[4, 5, 6]
|
41
|
+
[7, null, null]
|
42
|
+
|
43
|
+
>>> inGroupsOf [1..3], 2, ' ', (group) -> cout group
|
44
|
+
[1, 2]
|
45
|
+
[3, " "]
|
46
|
+
|
47
|
+
>>> inGroupsOf [1..3], 2, false, (group) -> cout group
|
48
|
+
[1, 2]
|
49
|
+
[3]
|
50
|
+
###
|
51
|
+
@inGroupsOf = (array, number, fillWith = null) =>
|
52
|
+
return array if number < 1
|
53
|
+
iterator = @blockGiven arguments
|
54
|
+
unless fillWith is false
|
55
|
+
# size % number gives how many extra we have;
|
56
|
+
# subtracting from number gives how many to add;
|
57
|
+
# modulo number ensures we don't add group of just fill.
|
58
|
+
padding = (number - array.length % number) % number
|
59
|
+
if padding
|
60
|
+
fillWith = null if _.isFunction fillWith
|
61
|
+
array = array.slice()
|
62
|
+
array.push fillWith while padding-- > 0
|
63
|
+
@eachSlice array, number, iterator
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
@is_uri = (path) ->
|
3
|
+
/^[-a-z]+:\/\/|^cid:|^\/\//.test path
|
4
|
+
|
5
|
+
@without_extension = (source) ->
|
6
|
+
source.replace /^(.+)(\.\w+)$/, '$1'
|
7
|
+
|
8
|
+
@rewrite_extension = (source, ext) =>
|
9
|
+
"#{@without_extension source}.#{ext}"
|
10
|
+
|
11
|
+
@ASSET_ID = ''
|
12
|
+
@asset_ids_cache = {}
|
13
|
+
# Use the ASSET_ID inscope variable or the random hash as its cache-busting asset id.
|
14
|
+
@asset_id = (source) =>
|
15
|
+
if _.isString @ASSET_ID
|
16
|
+
@ASSET_ID
|
17
|
+
else
|
18
|
+
@asset_ids_cache[source] or (@asset_ids_cache[source] = 10000000 + Math.floor(Math.random() * 90000000))
|
19
|
+
|
20
|
+
# Break out the asset path rewrite in case plugins wish to put the asset id
|
21
|
+
# someplace other than the query string.
|
22
|
+
@rewrite_asset_path = (source, dir) =>
|
23
|
+
source = "/#{dir}/#{source}" unless source[0] is '/'
|
24
|
+
if id = @asset_id(source)
|
25
|
+
"#{source}?#{id}"
|
26
|
+
else
|
27
|
+
source
|
28
|
+
|
29
|
+
@rewrite_relative_url_root = (source, relative_url_root) ->
|
30
|
+
if relative_url_root and not _.startsWith(source, "#{relative_url_root}/")
|
31
|
+
"#{relative_url_root}#{source}"
|
32
|
+
else
|
33
|
+
source
|
34
|
+
|
35
|
+
@RELATIVE_URL_ROOT = ''
|
36
|
+
|
37
|
+
@compute_public_path = (source, dir, options = {}) =>
|
38
|
+
return source if is_uri(source)
|
39
|
+
source = @rewrite_extension(source, options['ext']) if options['ext']
|
40
|
+
source = @rewrite_asset_path(source, dir)
|
41
|
+
source = @rewrite_relative_url_root(source, @RELATIVE_URL_ROOT)
|
42
|
+
source
|
43
|
+
|
44
|
+
@image_path = (source) =>
|
45
|
+
@compute_public_path(source, 'images')
|
46
|
+
|
47
|
+
@path_to_image = @image_path # aliased to avoid conflicts with an image_path named route
|
48
|
+
|
49
|
+
@basename = (source) =>
|
50
|
+
source = matches[2] if matches = source.match /^(.*\/)?(.+)?$/
|
51
|
+
|
52
|
+
@image_alt = (src) =>
|
53
|
+
_.capitalize @without_extension(@basename(src))
|
54
|
+
|
55
|
+
@image_tag = (source, options = {}) =>
|
56
|
+
src = options['src'] = @path_to_image source
|
57
|
+
unless options['alt'] or /^cid:/.test src
|
58
|
+
options['alt'] = @image_alt(src)
|
59
|
+
if size = _delete(options, 'size')
|
60
|
+
if matches = size.match /^(\d+)x(\d+)$/
|
61
|
+
options['width'] = matches[1]
|
62
|
+
options['height'] = matches[2]
|
63
|
+
tag 'img', options
|
@@ -0,0 +1,14 @@
|
|
1
|
+
@g_decor_body = (tag, inner) =>
|
2
|
+
@content_tag(tag, '', class: 'left') +
|
3
|
+
@content_tag(tag, '', class: 'right') +
|
4
|
+
@content_tag(tag, inner, class: 'fill')
|
5
|
+
|
6
|
+
@g_star_decor_body = (tag, inner) =>
|
7
|
+
@content_tag(tag, inner, class: 'wrapper') +
|
8
|
+
@content_tag(tag, '', class: 'corner left top') +
|
9
|
+
@content_tag(tag, '', class: 'corner right top') +
|
10
|
+
@content_tag(tag, '', class: 'corner left bottom') +
|
11
|
+
@content_tag(tag, '', class: 'corner right bottom')
|
12
|
+
|
13
|
+
@g_wrapper_decor_body = (tag, inner) =>
|
14
|
+
@content_tag tag, inner, class: 'wrapper'
|
File without changes
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# The DOM class convention is to use the singular form of an object or class. Examples:
|
2
|
+
#
|
3
|
+
# dom_class(post) # => "post"
|
4
|
+
# dom_class(Person) # => "person"
|
5
|
+
#
|
6
|
+
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
|
7
|
+
#
|
8
|
+
# dom_class(post, :edit) # => "edit_post"
|
9
|
+
# dom_class(Person, :edit) # => "edit_person"
|
10
|
+
@dom_class = (record_or_class, prefix = "") ->
|
11
|
+
singular = _.singular(record_or_class)
|
12
|
+
if prefix then "#{prefix}_#{singular}" else singular
|
13
|
+
|
14
|
+
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
|
15
|
+
# If no id is found, prefix with "new_" instead. Examples:
|
16
|
+
#
|
17
|
+
# dom_id(Post.find(45)) # => "post_45"
|
18
|
+
# dom_id(Post.new) # => "new_post"
|
19
|
+
#
|
20
|
+
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
|
21
|
+
#
|
22
|
+
# dom_id(Post.find(45), :edit) # => "edit_post_45"
|
23
|
+
# TODO sync with rorId and ror_id
|
24
|
+
@dom_id = (record, prefix = "") =>
|
25
|
+
if record_id = @record_key_for_dom_id(record)
|
26
|
+
"#{@dom_class(record, prefix)}_#{record_id}"
|
27
|
+
else
|
28
|
+
@dom_class(record, prefix or "new")
|
29
|
+
|
30
|
+
@record_key_for_dom_id = (record) ->
|
31
|
+
record = record.to_model() if _.isFunction(record.to_model)
|
32
|
+
key = record.to_key()
|
33
|
+
if key then key.join("_") else key
|
34
|
+
|
35
|
+
# TODO more zen features: +, *x, {content}
|
36
|
+
# TODO cache
|
37
|
+
@selectorToHtml = (selector) =>
|
38
|
+
if matches = selector.match(/^[\s>]*([\w\.]+)(.*)$/)
|
39
|
+
selector = matches[1]
|
40
|
+
continuation = matches[2]
|
41
|
+
classes = selector.split(".")
|
42
|
+
tag_name = classes.shift() or "div"
|
43
|
+
html_options = {}
|
44
|
+
html_options["class"] = classes.join(" ") if classes.length
|
45
|
+
if continuation
|
46
|
+
@content_tag(tag_name, @selectorToHtml(continuation), html_options)
|
47
|
+
else
|
48
|
+
@tag(tag_name, html_options)
|
49
|
+
else
|
50
|
+
""
|
51
|
+
|
52
|
+
@concat_class = =>
|
53
|
+
@warning "concat_class()", "refactoring with underscore"
|
54
|
+
@uniq(@compact(arguments).join(" ").split(/\s+/)).join(" ")
|
55
|
+
|
56
|
+
@html_options_to_s = (html_options) =>
|
57
|
+
if $.isPlainObject(html_options)
|
58
|
+
(" #{key}=\"#{value}\"" for key, value of html_options when typeof value isnt "undefined").join("")
|
59
|
+
else
|
60
|
+
""
|
61
|
+
|
62
|
+
@tag = (tag_name, html_options = {}) =>
|
63
|
+
"<#{tag_name}#{@html_options_to_s html_options} />"
|
64
|
+
|
65
|
+
@content_tag = (tag_name, content, html_options) =>
|
66
|
+
"<#{tag_name}#{@html_options_to_s html_options}>#{content}</#{tag_name}>"
|
67
|
+
|
68
|
+
@link_to = (body, url, html_options = {}) =>
|
69
|
+
html_options["href"] = url
|
70
|
+
@content_tag("a", body, html_options)
|
@@ -0,0 +1,149 @@
|
|
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
|
+
@WARNINGS ?= true
|
11
|
+
|
12
|
+
@cout = =>
|
13
|
+
args = @args 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 'warn', "\"#{subject}\" DEPRECATED!" + if instead then " Use instead: \"#{instead}\"" else ''
|
29
|
+
return
|
30
|
+
|
31
|
+
@warning = (subject, instead = null) =>
|
32
|
+
if @WARNINGS
|
33
|
+
@cout 'warn', "\"#{subject}\" WARNING!" + if instead then " Use instead: \"#{instead}\"" else ''
|
34
|
+
return
|
35
|
+
|
36
|
+
@args = (a) ->
|
37
|
+
r = []
|
38
|
+
Array::push.apply r, a if a.length > 0
|
39
|
+
r
|
40
|
+
|
41
|
+
@blockGiven = (args) =>
|
42
|
+
l = args.length
|
43
|
+
if l
|
44
|
+
l = args[l - 1]
|
45
|
+
if _.isFunction l then l else false
|
46
|
+
else
|
47
|
+
false
|
48
|
+
|
49
|
+
@_delete = (object, key) ->
|
50
|
+
value = object[key]
|
51
|
+
delete object[key]
|
52
|
+
value
|
53
|
+
|
54
|
+
# Helper function to get a value from a object as a property or as a function.
|
55
|
+
@getValue = (object, prop) ->
|
56
|
+
return null unless object and object[prop]
|
57
|
+
return if _.isFunction(object[prop]) then object[prop]() else object[prop]
|
58
|
+
|
59
|
+
@isset = (obj) =>
|
60
|
+
@deprecate 'isset(obj)', 'obj isnt undefined" OR "obj?'
|
61
|
+
obj isnt undefined
|
62
|
+
|
63
|
+
@isString = (v) =>
|
64
|
+
@deprecate 'isString(v)', '_.isString v'
|
65
|
+
_.isString v
|
66
|
+
|
67
|
+
@isNumber = (v) =>
|
68
|
+
@deprecate 'isNumber(v)', '_.isNumber v'
|
69
|
+
not isNaN parseInt v
|
70
|
+
|
71
|
+
@isJQ = (obj) ->
|
72
|
+
@deprecate 'isJQ(obj)', 'obj instanceof jQuery'
|
73
|
+
_.isObject(obj) and _.isString obj.jquery
|
74
|
+
|
75
|
+
@uniq = (arrOrString) ->
|
76
|
+
@deprecate 'uniq(a)', '_.uniq a'
|
77
|
+
isStr = _.isString arrOrString
|
78
|
+
return arrOrString unless isStr or _.isArray arrOrString
|
79
|
+
r = []
|
80
|
+
r.push e for e in arrOrString when not _.include r, e
|
81
|
+
if isStr then r.join '' else r
|
82
|
+
|
83
|
+
@logicalXOR = (a, b) ->
|
84
|
+
( a and not b ) or ( not a and b )
|
85
|
+
|
86
|
+
@bound = (number, min, max) ->
|
87
|
+
Math.max min, Math.min number, max
|
88
|
+
|
89
|
+
@roundWithPrecision = (number, precision = 2) ->
|
90
|
+
precision = Math.pow 10, precision
|
91
|
+
Math.round(number * precision) / precision
|
92
|
+
|
93
|
+
@regexpSpace = new RegExp '^\\s*$'
|
94
|
+
@regexpTrim = new RegExp '^\\s*(.*?)\\s*$'
|
95
|
+
|
96
|
+
@strTrim = (s) =>
|
97
|
+
@deprecate "strTrim(s)", "$.trim(s)"
|
98
|
+
s.match(@regexpTrim)[1]
|
99
|
+
|
100
|
+
# !!!!!!!!!!! tags !!!!!!!!!!!!
|
101
|
+
|
102
|
+
@number_to_currency = (number, units) ->
|
103
|
+
precision = 2
|
104
|
+
s = parseFloat(number).toFixed precision
|
105
|
+
b = s.length - 1 - precision
|
106
|
+
r = s.substring b
|
107
|
+
while b > 0
|
108
|
+
a = b - 3
|
109
|
+
r = ' ' + s.substring(a, b) + r
|
110
|
+
b = a
|
111
|
+
"#{r.substring(1)} #{units}"
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
@getParams = ->
|
116
|
+
q = location.search.substring(1).split '&'
|
117
|
+
r = {}
|
118
|
+
for e in q
|
119
|
+
t = e.split '='
|
120
|
+
r[t[0]] = t[1]
|
121
|
+
r
|
122
|
+
|
123
|
+
@respondFormat = (url, format = null) ->
|
124
|
+
aq = url.split '?'
|
125
|
+
ah = aq[0].split '#'
|
126
|
+
ad = ah[0].split '.'
|
127
|
+
currentFormat = if ad.length > 1 and not /\//.test(ad[ad.length - 1]) then ad.pop() else ''
|
128
|
+
return currentFormat unless format?
|
129
|
+
return url if format is currentFormat
|
130
|
+
ad.push format if format
|
131
|
+
ah[0] = ad.join '.'
|
132
|
+
aq[0] = ah.join '#'
|
133
|
+
aq.join '?'
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
@rails_data = {}
|
138
|
+
|
139
|
+
@rails_scope = (controller_name, action_name, scopedCloasure = null, scopedCloasureArguments...) =>
|
140
|
+
return false if _.isString(controller_name) and controller_name isnt @rails_data['controller_name']
|
141
|
+
return false if _.isString(action_name) and action_name isnt @rails_data['action_name']
|
142
|
+
if _.isFunction scopedCloasure
|
143
|
+
arguments[2] scopedCloasureArguments...
|
144
|
+
true
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
$ =>
|
149
|
+
@rails_data = $('body').data()
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
@unlazy_datepickers = (jRoot) ->
|
3
|
+
jRoot.find('input.datepicker.lazy').removeClass('lazy').length
|
4
|
+
|
5
|
+
@bind_datepickers = (jRoot) ->
|
6
|
+
jRoot.find('input.datepicker:not(.lazy, .hasDatepicker)').each( ->
|
7
|
+
jThis = $ @
|
8
|
+
opts =
|
9
|
+
'showAnim' : ''
|
10
|
+
'showOn' : 'both'
|
11
|
+
'buttonImage' : '/images/forms/datepicker__icon.png' # TODO forwarding assets path
|
12
|
+
'buttonImageOnly': true
|
13
|
+
'changeMonth' : true
|
14
|
+
'changeYear' : true
|
15
|
+
'onClose' : (dateText, inst) -> $(inst.input).change().focusout()
|
16
|
+
opts[_.camelize(k)] = d for k, d of jThis.data()
|
17
|
+
jThis.datepicker opts
|
18
|
+
).length
|
19
|
+
|
20
|
+
$.fn.orig_datepicker = $.fn.datepicker
|
21
|
+
|
22
|
+
$.fn.datepicker = ->
|
23
|
+
return @ unless @length
|
24
|
+
@data('changed', true) if arguments.length and arguments[0] is 'setDate'
|
25
|
+
$.fn.orig_datepicker.apply @, arguments
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
$ ->
|
30
|
+
|
31
|
+
$('body').on 'keyup keydown', 'input.datepicker.hasDatepicker', ->
|
32
|
+
$(@).datepicker 'hide'
|
33
|
+
|
34
|
+
bind_datepickers $('body')
|