active_admin_date_range_preset 0.3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e2eafd8e73beed25a8010bbe309291da805ef1e
4
+ data.tar.gz: eaf3f1a3bc3c02267c2b84d14fa4b04b934e9bce
5
+ SHA512:
6
+ metadata.gz: 42111b783d917407effbab879d4e6aedc3abf37ffe77e3394af742188d0ace77e99280f0b3edd9404a7be6bc93b6b3e705b0b99ddfa77d4fa1993616acd573e7
7
+ data.tar.gz: cdf6a722273e792c3b66dcb9e11d32788de15ee440fab011a9c11f9262bd8c24c64e52824db1d1fd54be3d7b8e781de927ff1ee7ab46d9ee21a253618b53ac74
data/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in active_admin_date_range_preset.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Igor Fedoronchuk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # active_admin_date_range_preset
2
+
3
+ Preset links for ActiveAdmin date_range inputs in sidebar filters in forms
4
+
5
+ This is how it looks like
6
+
7
+ ![Step 1](/screen/step_1.jpg)
8
+
9
+ ![Step 2](/screen/step_2.jpg)
10
+
11
+ ![Form 1](/screen/step_2_1.png)
12
+
13
+ ![Form 2](/screen/step_2_2.png)
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'active_admin_date_range_preset', github: 'workgena/active_admin_date_range_preset'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle install
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install active_admin_date_range_preset
30
+
31
+ Include assests:
32
+
33
+ JS asset
34
+ ```//= require active_admin_date_range_preset```
35
+
36
+ CSS
37
+ ```@import "active_admin_date_range_preset";```
38
+
39
+ Your sidebar filters should now have link "Set Range"
40
+
41
+ ## Usage
42
+
43
+ in New/Edit formtastic forms:
44
+
45
+ ```ruby
46
+ f.input :date_from, as: :date_time_picker, wrapper_html: { class: 'datetime_preset_pair', data: { show_time: 'true' } }
47
+ f.input :date_to, as: :date_time_picker
48
+ ```
49
+
50
+ Input can be "as :string" or any other type compatible with "input type=text"
51
+ Main point is to set for first input-pair wrapper-class
52
+
53
+ ```wrapper_html: { class: 'datetime_preset_pair' }```
54
+
55
+ input name('date_from' and 'date_to') can be named whatever your need
56
+
57
+ By default inputs are filled with date("yyyy-mm-dd"). If you need time add
58
+
59
+ ```data: { show_time: 'true' }```
60
+
61
+
62
+ ## Using with ActiveAdminDatetimepicker
63
+
64
+ If you use GEM https://github.com/activeadmin-plugins/active_admin_datetimepicker
65
+ and want apply this plugin to filter-inputs for this gem you need:
66
+
67
+ First apply ActiveAdminDatetimepicker to any filters your need
68
+
69
+ ```ruby
70
+ filter :time_start, as: :date_time_range
71
+ ```
72
+
73
+ In active_admin.js
74
+
75
+ Add following lines to JavaScript
76
+
77
+ ```javascript
78
+ $(document).on('ready', function(){
79
+ $('form.filter_form div.filter_date_time_range').date_range_ext_preset();
80
+ });
81
+ ```
82
+
83
+ Now all you "date_time_range" inputs has button "Set range"
84
+
85
+
86
+ ## Custom usage
87
+
88
+ You can assign "Set range" almost to any input-text-pair filters/forms.
89
+ For example, you have complex form where input-pairs are not close to each other and not standard.
90
+
91
+ ```javascript
92
+ $(document).on('ready', function(){
93
+ $('.any_jquery_selector').date_range_ext_preset({
94
+ lteq_input: '.jquery_selector_to_first_input',
95
+ gteq_input: '.jquery_selector_to_second_input'
96
+ });
97
+ });
98
+ ```
99
+ ".any_jquery_selector" is pointed to place where button "Set range" will appear.
100
+ Set lteq_input and gteq_input to point to inputs if they not near main selector.
101
+
102
+
103
+ ## Global and local settings
104
+
105
+ There are several settings, which can be set globally or locally.
106
+
107
+ Example how to set settings for only specific inputs
108
+
109
+ ```javascript
110
+ $('.any_jquery_selector').date_range_ext_preset({
111
+ setting_name: "setting_value"
112
+ });
113
+ ```
114
+ Example how to set global settings. Write it before $(document).on('ready')
115
+
116
+ ```javascript
117
+ $.fn.date_range_ext_preset.defaults.setting_name = "setting_value"
118
+ ```
119
+
120
+ You can set global defaults in your active_admin.js like this:
121
+
122
+ ```javascript
123
+ # End date will be full-day, not next.
124
+ # Today true : 2015-06-12 - 2015-06-12
125
+ # Today false: 2015-06-12 - 2015-06-13
126
+ $.fn.date_range_ext_preset.defaults.date_to_human_readable = true
127
+
128
+ # Display time
129
+ # Today: 2015-06-12 00:00:00 - 2015-06-13 00:00:00
130
+ # Today with human_readable=true: 2015-06-12 00:00:00 - 2015-16-12 23:59:59
131
+ $.fn.date_range_ext_preset.defaults.show_time = true
132
+ ```
133
+
134
+ ### date_to_human_readable
135
+
136
+ value: true/false
137
+
138
+ default: false
139
+
140
+ This options changes second date to include full date-time of the day, like normal human thinks about time ranges.
141
+ Today true : 2015-06-12 - 2015-06-12
142
+ Today false: 2015-06-12 - 2015-06-13
143
+
144
+ When normal human say "2015-06-12" hi means "2015-06-12 23:59:59"
145
+ But default behavior in programming is "2015-06-12" = "2015-06-12 00:00:00"
146
+ Be careful with this options. Cause if you change it to "true" you will also need to change your server-side scripts to search "humanize-way".
147
+
148
+ ### show_time
149
+
150
+ value: true/false
151
+
152
+ default: false
153
+
154
+ If true then will show date and time, usually it will be 00:00:00
155
+
156
+ ### hours_offset
157
+
158
+ values: positive or negative integer
159
+
160
+ default: 0
161
+
162
+ To work correctly this plugin needs to detect current date-time. And it uses UTC. But if you need your local timezone or some other time-shift, your can set this option:
163
+
164
+ Example:
165
+ ```javascript
166
+ $.fn.date_range_ext_preset.defaults.hours_offset = +3
167
+ // or
168
+ $.fn.date_range_ext_preset.defaults.hours_offset = -3
169
+ ```
170
+
171
+ ### Addition ranges
172
+
173
+ ```javascript
174
+ $(document).on('ready', function(){
175
+
176
+ $('.filter_form .filter_date_range').date_range_ext_preset({
177
+ date_to_human_readable: true, # affects last day
178
+ add_range: [
179
+ {
180
+ title: 'Last 30 days',
181
+ // date_to_human_readable affects end-date, sow must do this:
182
+ start: new Date(new Date().setDate(new Date().getDate() - 29)),
183
+ end: new Date(new Date().setDate(new Date().getDate() + 1))
184
+ }
185
+ ]
186
+ });
187
+
188
+ });
189
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_admin_date_range_preset/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_admin_date_range_preset"
8
+ spec.version = ActiveAdminDateRangePreset::VERSION
9
+ spec.authors = ["Gena M."]
10
+ spec.email = ["workgena@gmail.com"]
11
+
12
+ spec.summary = %q{date_range_preset extension for ActiveAdmin}
13
+ spec.description = %q{Integrate useful fast links to set date ranges in to ActiveAdmin, for example today range, week range, month range}
14
+ spec.homepage = "https://github.com/workgena/active_admin_date_range_preset"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.8"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveAdminDateRangePreset
2
+ VERSION = "0.3.0"
3
+ end
@@ -0,0 +1,11 @@
1
+ require "activeadmin"
2
+ require "active_admin_date_range_preset/version"
3
+
4
+ module ActiveAdminDateRangePreset
5
+ module Rails
6
+ class Engine < ::Rails::Engine
7
+
8
+ end
9
+ end
10
+
11
+ end
data/screen/step_1.jpg ADDED
Binary file
data/screen/step_2.jpg ADDED
Binary file
Binary file
Binary file
@@ -0,0 +1,191 @@
1
+ (($) ->
2
+ # options:
3
+ # gteq_input: jQuery-selecotr for input date_from
4
+ # lteq_input: jQuery-selecotr for input date_to
5
+ # hours_offset: Int number - hours +/- to correct time
6
+ $.fn.date_range_ext_preset = (options)->
7
+
8
+ # settings
9
+ options = options || {}
10
+ opts = $.extend( {}, $.fn.date_range_ext_preset.defaults, options );
11
+
12
+ # aditional functions
13
+ num_with_leading_zero = (num, digits_count = 2)->
14
+ s = num + ''
15
+ s = '0' + s while (s.length < digits_count)
16
+ return s
17
+
18
+ # formated date YYYY-MM-DD, with converting to UTC
19
+ # note: getMonth Returns the month (from 0-11), so we do +1
20
+ formatDate = (date)->
21
+ str = date.getFullYear() + '-' + num_with_leading_zero(date.getMonth()+1) + '-' + num_with_leading_zero(date.getDate())
22
+ if opts.show_time
23
+ str += ( ' ' + num_with_leading_zero(date.getHours()) + ':' + num_with_leading_zero(date.getMinutes()) + ':' + num_with_leading_zero(date.getSeconds()) )
24
+ return str
25
+
26
+ unbindClickEventBlockTimerange = ->
27
+ $('.block_timerange').remove()
28
+ $('body').off('click.CalendarRangeSet')
29
+
30
+ # local datetime now
31
+ now = new Date()
32
+ # UTC datetime now
33
+ now_utc = new Date(
34
+ now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
35
+ now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(),
36
+ now.getUTCMilliseconds()
37
+ )
38
+ # datetime object, UTC with hours offset
39
+ datetime = new Date(now_utc.getTime() + opts.hours_offset * 60 * 60 * 1000)
40
+
41
+
42
+ # PLUGIN BEGIN
43
+ return this.each (i, el) ->
44
+ $this = $(el)
45
+
46
+ if typeof $this.data('show-time') != 'undefined' && $this.data('show-time').toString() == 'true'
47
+ console.log $this.data('show-time')
48
+ opts.show_time = true
49
+
50
+
51
+ # detect inputs
52
+ if options.lteq_input
53
+ lteq_input = $(options.lteq_input)
54
+ else
55
+ lteq_input = if $this.hasClass('datetime_preset_pair') then $this.next().find('input') else $this.find('input:last')
56
+ if options.gteq_input
57
+ gteq_input = $(options.gteq_input)
58
+ else
59
+ gteq_input = if $this.hasClass('datetime_preset_pair') then $this.find('input') else $this.find('input:first')
60
+
61
+ # filter modifying
62
+ main_btn_html = '<a href="#" class="btn_timerange">Set range</a>'
63
+ $this.find('label').addClass('datetime_preset_filter_label').append(main_btn_html)
64
+
65
+ # helper
66
+ fillInputs = (start, end)->
67
+ gteq_input.val(formatDate(start) )
68
+ if opts.date_to_human_readable
69
+ end.setTime( end.getTime() - 1000 )
70
+ lteq_input.val( formatDate(end) )
71
+
72
+ $this.on('click', '.btn_timerange', (e)->
73
+ unbindClickEventBlockTimerange()
74
+ e.stopPropagation()
75
+ e.preventDefault()
76
+
77
+ additional_items_html = ''
78
+ opts.add_range.forEach (el, i)->
79
+ additional_items_html += '<div><span class="btn_date_range_' + i + '">' + el['title'] + '</span></div>'
80
+
81
+ $('body').append('<div style="min-width: '+e.target.offsetWidth+'px; top: '+(e.target.offsetTop)+'px; left: '+(e.target.offsetLeft)+'px" class="block_timerange">' +
82
+ '<div><span class="btn_today">Today</span></div>' +
83
+ '<div><span class="btn_yesterday">Yesterday</span></div>' +
84
+ '<div><span class="btn_week">This Week</span></div>' +
85
+ '<div><span class="btn_month">This Month</span></div>' +
86
+ '<div><span class="btn_last_week">Last Week</span></div>' +
87
+ '<div><span class="btn_last_month">Last Month</span></div>' +
88
+ additional_items_html +
89
+ '</div>'
90
+ ).ready(->
91
+ container = $(this).find('.block_timerange')
92
+
93
+ # additional ranges
94
+ opts.add_range.forEach (el, i)->
95
+ $(container).on('click.CalendarRangeSet', '.btn_date_range_' + i, (e)->
96
+ unbindClickEventBlockTimerange()
97
+ start = new Date(el['start'].getFullYear(), el['start'].getMonth(), el['start'].getDate())
98
+ end = new Date(el['end'].getFullYear(), el['end'].getMonth(), el['end'].getDate())
99
+ fillInputs(start, end)
100
+ )
101
+
102
+ # Today
103
+ $(container).on('click.CalendarRangeSet', '.btn_today', (e)->
104
+ unbindClickEventBlockTimerange()
105
+ start = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate())
106
+ end = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() + 1)
107
+ fillInputs(start, end)
108
+ )
109
+
110
+ # Yesterday
111
+ $(container).on('click.CalendarRangeSet', '.btn_yesterday', (e)->
112
+ unbindClickEventBlockTimerange()
113
+ start = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() - 1)
114
+ end = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate())
115
+ fillInputs(start, end)
116
+ )
117
+
118
+ # Week
119
+ $(container).on('click.CalendarRangeSet', '.btn_week', (e)->
120
+ unbindClickEventBlockTimerange()
121
+ start = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() - datetime.getDay() + 1)
122
+ end = new Date(start.getFullYear(), start.getMonth(), start.getDate() + 7)
123
+ fillInputs(start, end)
124
+ )
125
+
126
+ # Month
127
+ $(container).on('click.CalendarRangeSet', '.btn_month', (e)->
128
+ unbindClickEventBlockTimerange()
129
+ start = new Date(datetime.getFullYear(), datetime.getMonth(), 1)
130
+ end = new Date(datetime.getFullYear(), datetime.getMonth()+1, 1)
131
+ fillInputs(start, end)
132
+ )
133
+
134
+ # Last Week
135
+ $(container).on('click.CalendarRangeSet', '.btn_last_week', (e)->
136
+ unbindClickEventBlockTimerange()
137
+ end = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() - datetime.getDay() + 1)
138
+ start = new Date(end.getFullYear(), end.getMonth(), end.getDate() - 7)
139
+ fillInputs(start, end)
140
+ )
141
+
142
+ # Last Month
143
+ $(container).on('click.CalendarRangeSet', '.btn_last_month', (e)->
144
+ unbindClickEventBlockTimerange()
145
+ end = new Date(datetime.getFullYear(), datetime.getMonth(), 1)
146
+ start = new Date(end.getFullYear(), end.getMonth() - 1, 1)
147
+ fillInputs(start, end)
148
+ )
149
+
150
+ # Outer
151
+ $('body').on('click.CalendarRangeSet', (e)->
152
+ e.stopPropagation()
153
+ if $(e.target).closest('.block_timerange').length == 0
154
+ unbindClickEventBlockTimerange()
155
+ )
156
+ )
157
+ )
158
+
159
+ $.fn.date_range_ext_preset.defaults = {
160
+ # Manual global time shift, from UTC can be +/- number
161
+ hours_offset: 0,
162
+
163
+ # date_to_human_readable = true, then "date_to" consider as including full day without last second
164
+ # For example Today will be:
165
+ # true
166
+ # 2015-06-10 - 2015-06-10
167
+ # 2015-06-10 00:00:00 - 2015-06-10 23:59:59
168
+ # false
169
+ # 2015-06-10 - 2015-06-11
170
+ # 2015-06-10 00:00:00 - 2015-06-11 00:00:00
171
+ date_to_human_readable: false,
172
+
173
+ # Display time or not: 2015-06-10 vs 2015-06-10 00:00:00
174
+ show_time: false,
175
+
176
+ # Array of addition ranges
177
+ # example:
178
+ # {
179
+ # title: 'Last 30 days',
180
+ # start: new Date().setDate((new Date()).getDate() - 30)
181
+ # end: new Date()
182
+ # }
183
+ add_range: []
184
+ };
185
+
186
+ ) jQuery
187
+
188
+
189
+ $(document).on 'ready', ->
190
+ # Init in forms
191
+ $('.datetime_preset_pair').date_range_ext_preset()
@@ -0,0 +1,36 @@
1
+ form label.datetime_preset_filter_label .btn_timerange {
2
+ float: right;
3
+ margin: -2px 8px 0 0;
4
+ text-decoration: none;
5
+ border-bottom: 1px dashed;
6
+ line-height: 1.4em;
7
+ &:hover {
8
+ border-bottom: 0 none;
9
+ }
10
+ }
11
+
12
+ .block_timerange {
13
+ position: absolute;
14
+ display: block;
15
+ border: 1px solid #000;
16
+ padding: 5px 10px 5px 10px;
17
+ text-align: center;
18
+ background-color: #f4f4f4;
19
+ >div {
20
+ margin-top: 10px;
21
+ &:last-child {
22
+ margin-bottom: 10px;
23
+ }
24
+ span {
25
+ color: #2e86cc;
26
+ cursor: pointer;
27
+ text-decoration: none;
28
+ font-size: 14px;
29
+ border-bottom: 1px dashed;
30
+ &:hover {
31
+ border-bottom: 0 none;
32
+ }
33
+ }
34
+ }
35
+ }
36
+
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_admin_date_range_preset
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Gena M.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Integrate useful fast links to set date ranges in to ActiveAdmin, for
42
+ example today range, week range, month range
43
+ email:
44
+ - workgena@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".idea/vcs.xml"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - active_admin_date_range_preset.gemspec
55
+ - lib/active_admin_date_range_preset.rb
56
+ - lib/active_admin_date_range_preset/version.rb
57
+ - screen/step_1.jpg
58
+ - screen/step_2.jpg
59
+ - screen/step_2_1.png
60
+ - screen/step_2_2.png
61
+ - vendor/assets/javascripts/active_admin_date_range_preset.coffee
62
+ - vendor/assets/stylesheets/active_admin_date_range_preset.css.scss
63
+ homepage: https://github.com/workgena/active_admin_date_range_preset
64
+ licenses:
65
+ - MIT
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.6.10
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: date_range_preset extension for ActiveAdmin
87
+ test_files: []