wice_grid_mongoid 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG +409 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +1172 -0
  5. data/Rakefile +42 -0
  6. data/SAVED_QUERIES_HOWTO.rdoc +123 -0
  7. data/VERSION +1 -0
  8. data/generators/common_templates/icons/arrow_down.gif +0 -0
  9. data/generators/common_templates/icons/arrow_up.gif +0 -0
  10. data/generators/common_templates/icons/calendar_view_month.png +0 -0
  11. data/generators/common_templates/icons/delete.png +0 -0
  12. data/generators/common_templates/icons/expand.png +0 -0
  13. data/generators/common_templates/icons/page_white_excel.png +0 -0
  14. data/generators/common_templates/icons/page_white_find.png +0 -0
  15. data/generators/common_templates/icons/table.png +0 -0
  16. data/generators/common_templates/icons/table_refresh.png +0 -0
  17. data/generators/common_templates/icons/tick_all.png +0 -0
  18. data/generators/common_templates/icons/untick_all.png +0 -0
  19. data/generators/common_templates/initializers/wice_grid_config.rb +215 -0
  20. data/generators/common_templates/locales/wice_grid.yml +269 -0
  21. data/generators/common_templates/stylesheets/wice_grid.css +173 -0
  22. data/generators/wice_grid_assets_jquery/templates/USAGE +6 -0
  23. data/generators/wice_grid_assets_jquery/templates/javascripts/wice_grid_jquery.js +161 -0
  24. data/generators/wice_grid_assets_jquery/wice_grid_assets_jquery_generator.rb +35 -0
  25. data/generators/wice_grid_assets_prototype/USAGE +8 -0
  26. data/generators/wice_grid_assets_prototype/templates/javascripts/calendarview.js +1168 -0
  27. data/generators/wice_grid_assets_prototype/templates/javascripts/wice_grid_prototype.js +153 -0
  28. data/generators/wice_grid_assets_prototype/templates/stylesheets/calendarview.css +107 -0
  29. data/generators/wice_grid_assets_prototype/wice_grid_assets_prototype_generator.rb +37 -0
  30. data/init.rb +1 -0
  31. data/install.rb +1 -0
  32. data/lib/grid_output_buffer.rb +52 -0
  33. data/lib/grid_renderer.rb +531 -0
  34. data/lib/helpers/js_calendar_helpers.rb +188 -0
  35. data/lib/helpers/wice_grid_misc_view_helpers.rb +113 -0
  36. data/lib/helpers/wice_grid_serialized_queries_view_helpers.rb +82 -0
  37. data/lib/helpers/wice_grid_view_helpers.rb +781 -0
  38. data/lib/js_adaptors/jquery_adaptor.rb +145 -0
  39. data/lib/js_adaptors/js_adaptor.rb +12 -0
  40. data/lib/js_adaptors/prototype_adaptor.rb +168 -0
  41. data/lib/table_column_matrix.rb +51 -0
  42. data/lib/view_columns.rb +469 -0
  43. data/lib/views/create.rjs +13 -0
  44. data/lib/views/delete.rjs +12 -0
  45. data/lib/wice_grid.rb +809 -0
  46. data/lib/wice_grid_controller.rb +165 -0
  47. data/lib/wice_grid_core_ext.rb +179 -0
  48. data/lib/wice_grid_misc.rb +99 -0
  49. data/lib/wice_grid_serialized_queries_controller.rb +77 -0
  50. data/lib/wice_grid_serialized_query.rb +14 -0
  51. data/lib/wice_grid_spreadsheet.rb +33 -0
  52. data/tasks/wice_grid_tasks.rake +28 -0
  53. data/test/.gitignore +2 -0
  54. data/test/database.yml +21 -0
  55. data/test/schema.rb +33 -0
  56. data/test/test_helper.rb +89 -0
  57. data/test/views/projects_and_people_grid.html.erb +12 -0
  58. data/test/views/projects_and_people_grid_invalid.html.erb +12 -0
  59. data/test/views/simple_projects_grid.html.erb +9 -0
  60. data/test/wice_grid_core_ext_test.rb +183 -0
  61. data/test/wice_grid_functional_test.rb +68 -0
  62. data/test/wice_grid_misc_test.rb +41 -0
  63. data/test/wice_grid_test.rb +42 -0
  64. data/test/wice_grid_view_helper_test.rb +12 -0
  65. data/uninstall.rb +1 -0
  66. data/wice_grid_mongoid.gemspec +111 -0
  67. metadata +141 -0
@@ -0,0 +1,173 @@
1
+ /* This is just an example, so feel free to re-style it completely */
2
+
3
+ .wice_grid {
4
+ border: 1px solid #ccc;
5
+ border-collapse: collapse;
6
+ border-spacing: 0px
7
+ }
8
+
9
+ .wice_grid .clickable{
10
+ cursor: pointer;
11
+ }
12
+
13
+ .wice_grid th {
14
+ background-color: #dfdfdf;
15
+ font-weight: bold;
16
+ text-shadow: rgb(204, 204, 204) 0px 1px 0px;
17
+ height: 2em;
18
+ padding: 0 1em;
19
+
20
+ }
21
+ .wice_grid th a {
22
+ color: #000;
23
+ }
24
+ .wice_grid th a:hover {
25
+ color: #333;
26
+ }
27
+
28
+ .wice_grid tr.wice_grid_title_row th,
29
+ .wice_grid tr.wice_grid_filter_row th{
30
+ background-color: #dddddd;
31
+ vertical-align: top;
32
+ }
33
+
34
+
35
+
36
+ .wice_grid th.filter_icons, .wice_grid th.hide_show_icon {
37
+ text-align: right ;
38
+ }
39
+
40
+ .wice_grid thead {
41
+ border-bottom: 1px solid #666;
42
+ }
43
+
44
+ .wice_grid tr.odd {
45
+ background-color: #fff;
46
+ }
47
+
48
+ .wice_grid tr.even {
49
+ background-color: #eeeeee;
50
+ }
51
+
52
+ .wice_grid td {
53
+ border-bottom: 1px dotted #9999CC;
54
+ height: 2.5em;
55
+ padding: 0 1em;
56
+ }
57
+
58
+ .wice_grid .desc, .wice_grid .asc{
59
+ padding-right: 18px;
60
+ text-decoration:none;
61
+ }
62
+
63
+ .wice_grid .desc {
64
+ background: transparent url(/images/icons/grid/arrow_down.gif) right no-repeat
65
+ }
66
+ .wice_grid .asc {
67
+ background: transparent url(/images/icons/grid/arrow_up.gif) right no-repeat
68
+ }
69
+
70
+ .wice_grid th div.date-filter {
71
+ text-align: left;
72
+ }
73
+
74
+ .wice_grid tr.even td.active_filter { background-color: #ddddff; }
75
+ .wice_grid tr.odd td.active_filter { background-color: #eeeeff; }
76
+
77
+ .wice_grid tr.even td.sorted { background-color: #ffffcc; }
78
+ .wice_grid tr.odd td.sorted { background-color: #ffffee; }
79
+
80
+
81
+ .wice_grid a.date_label {text-decoration: none;}
82
+ .wice_grid a.date_label:hover {text-decoration: line-through;}
83
+
84
+ .wice_grid td.sel{
85
+ text-align: center;
86
+ }
87
+
88
+ .wice_grid_container .pagination {
89
+ float: left;
90
+ }
91
+
92
+ .wice_grid_container .pagination_status {
93
+ font-weight: bold;
94
+ float: right;
95
+ }
96
+
97
+ .wice_grid_container .pagination_status .show_all_link a {
98
+ text-decoration: none;
99
+ font-size: 80%;
100
+ }
101
+
102
+ .wice_grid .custom_dropdown_container { position: relative; padding-right: 9px;}
103
+ .wice_grid .custom_dropdown_container .toggle_multi_select_icon { position:absolute; top:0; right:0; }
104
+
105
+ .wice_grid .text_filter_container { position: relative; padding-right: 13px;}
106
+ .wice_grid .text_filter_container .negation_checkbox { position:absolute; top:0; right:0; }
107
+
108
+
109
+ /* Saved Queries */
110
+
111
+ .wice_grid_container h3 {
112
+ font-family: 'Lucida Grande';
113
+ font-size: 16px;
114
+ font-style: normal;
115
+ font-weight: bold;
116
+ margin-bottom: 2px;
117
+ }
118
+
119
+ .wice_grid_query_panel {
120
+ width: 400px;
121
+ border:1px solid #ddd;
122
+ margin-top : 10px;
123
+ margin-bottom : 10px;
124
+ padding : 5px;
125
+ }
126
+
127
+
128
+ .wice_grid_query_panel .query_list{
129
+ list-style-image: none;
130
+ list-style-position: outside;
131
+ list-style-type: none;
132
+ margin-left : 0px;
133
+ }
134
+
135
+ .wice_grid_query_panel .query_list li a.query_load_link {
136
+ text-decoration: none;
137
+ }
138
+
139
+
140
+ .wice_grid_query_panel .query_list li a.current {
141
+ font-weight: bold;
142
+ text-decoration: underline;
143
+ }
144
+
145
+
146
+ .wice_grid_query_panel .query_list li a.query_load_link:hover {
147
+ text-decoration: underline;
148
+ }
149
+
150
+
151
+ .wice_grid_query_panel .wice_grid_notice {
152
+ padding:.4em;
153
+ margin-bottom:1em;
154
+ border:2px solid #ddd;
155
+ background:#FFF6BF;
156
+ color:#514721;
157
+ border-color:#FFD324;
158
+ }
159
+
160
+
161
+ .wice_grid_query_panel .errorExplanation {
162
+ padding:.4em;
163
+ margin-bottom:1em;
164
+ border:2px solid #ddd;
165
+ background:#FBE3E4;
166
+ color:#8a1f11;
167
+ border-color:#FBC2C4;
168
+ list-style-image: none;
169
+ list-style-position: outside;
170
+ list-style-type: none;
171
+ margin-left : 0px;
172
+ }
173
+
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Copies
3
+ configuration file wice_grid_config.rb to config/initializers/
4
+ wice_grid_jquery.js to public/javascripts/wice_grid.js
5
+ wice_grid.css to public/stylesheets/
6
+ wice_grid images to public/images/icons/grid
@@ -0,0 +1,161 @@
1
+ function WiceGridProcessor(name, base_request_for_filter, base_link_for_show_all_records,
2
+ link_for_export, parameter_name_for_query_loading, parameter_name_for_focus, environment){
3
+
4
+ this.checkIfJsFrameworkIsLoaded = function(){
5
+ if (! jQuery){
6
+ alert("jQuery not loaded, WiceGrid cannot proceed!")
7
+ }
8
+ }
9
+
10
+ this.checkIfJsFrameworkIsLoaded();
11
+ this.name = name;
12
+ this.parameter_name_for_query_loading = parameter_name_for_query_loading;
13
+ this.parameter_name_for_focus = parameter_name_for_focus;
14
+ this.base_request_for_filter = base_request_for_filter;
15
+ this.base_link_for_show_all_records = base_link_for_show_all_records;
16
+ this.link_for_export = link_for_export;
17
+ this.filter_declarations = new Array();
18
+ this.environment = environment;
19
+
20
+ this.toString = function(){
21
+ return "<WiceGridProcessor instance for grid '" + this.name + "'>";
22
+ }
23
+
24
+
25
+ this.process = function(dom_id_to_focus){
26
+ window.location = this.build_url_with_params(dom_id_to_focus);
27
+ }
28
+
29
+ this.reload_page_for_given_grid_state = function(grid_state){
30
+ var request_path = this.grid_state_to_request(grid_state);
31
+ window.location = this.append_to_url(this.base_link_for_show_all_records, request_path);
32
+ }
33
+
34
+ this.load_query = function(query_id){
35
+ var request = this.append_to_url(this.build_url_with_params(),
36
+ (this.parameter_name_for_query_loading + encodeURIComponent(query_id)));
37
+ window.location = request;
38
+ }
39
+
40
+ this.save_query = function(query_name, base_path_to_query_controller, grid_state, input_ids){
41
+ if (input_ids instanceof Array) {
42
+ input_ids.each(function(dom_id){
43
+ grid_state.push(['extra[' + dom_id + ']', $('#'+ dom_id)[0].value])
44
+ });
45
+ }
46
+
47
+ var request_path = this.grid_state_to_request(grid_state);
48
+
49
+ jQuery.ajax({
50
+ url: base_path_to_query_controller,
51
+ async: true,
52
+ data: request_path + '&query_name=' + encodeURIComponent(query_name),
53
+ dataType: 'script',
54
+ type: 'POST'
55
+ });
56
+ }
57
+
58
+ this.grid_state_to_request = function(grid_state){
59
+ return jQuery.map(grid_state, function(pair){
60
+ return encodeURIComponent(pair[0]) + '=' + encodeURIComponent(pair[1]);
61
+ }).join('&');
62
+ }
63
+
64
+
65
+ this.append_to_url = function(url, str){
66
+ var sep;
67
+ if (url.indexOf('?') != -1){
68
+ if (/[&\?]$/.exec(url)){
69
+ sep = '';
70
+ }else{
71
+ sep = '&';
72
+ }
73
+ }else{
74
+ sep = '?';
75
+ }
76
+ return url + sep + str;
77
+ }
78
+
79
+
80
+ this.build_url_with_params = function(dom_id_to_focus){
81
+ var results = new Array();
82
+ var _this = this;
83
+ jQuery.each(this.filter_declarations, function(i, filter_declaration){
84
+ param = _this.read_values_and_form_query_string(
85
+ filter_declaration.filter_name, filter_declaration.detached,
86
+ filter_declaration.templates, filter_declaration.ids);
87
+ if (param && param != ''){
88
+ results.push(param);
89
+ }
90
+ });
91
+
92
+ var res = this.base_request_for_filter;
93
+ if ( results.length != 0){
94
+ all_filter_params = results.join('&');
95
+ res = this.append_to_url(res, all_filter_params);
96
+ }
97
+ if (dom_id_to_focus){
98
+ res = this.append_to_url(res, this.parameter_name_for_focus + dom_id_to_focus);
99
+ }
100
+ return res;
101
+ }
102
+
103
+ this.reset = function(){
104
+ window.location = this.base_request_for_filter;
105
+ }
106
+
107
+ this.export_to_csv = function(){
108
+ window.location = this.link_for_export;
109
+ }
110
+
111
+ this.register = function(func){
112
+ this.filter_declarations.push(func);
113
+ }
114
+
115
+ this.read_values_and_form_query_string = function(filter_name, detached, templates, ids){
116
+ var res = new Array();
117
+ for(i = 0; i < templates.length; i++){
118
+ if($(ids[i]) == null){
119
+ if (this.environment == "development"){
120
+ message = 'WiceGrid: Error reading state of filter "' + filter_name + '". No DOM element with id "' + ids[i] + '" found.'
121
+ if (detached){
122
+ message += 'You have declared "' + filter_name +
123
+ '" as a detached filter but have not output it anywhere in the template. Read documentation about detached filters.'
124
+ }
125
+ alert(message);
126
+ }
127
+ return '';
128
+ }
129
+ var el = $('#' + ids[i]);
130
+ var val;
131
+ if (el[0] && el[0].type == 'checkbox'){
132
+ if (el[0].checked) val = 1;
133
+ } else {
134
+ val = el.val();
135
+ }
136
+ if (val instanceof Array) {
137
+ for(j = 0; j < val.length; j++){
138
+ if (val[j] && val[j] != "")
139
+ res.push(templates[i] + encodeURIComponent(val[j]));
140
+ }
141
+ } else if (val && val != ''){
142
+ res.push(templates[i] + encodeURIComponent(val));
143
+ }
144
+ }
145
+ return res.join('&');
146
+ }
147
+
148
+ };
149
+
150
+ function toggle_multi_select(select_id, link_obj, expand_label, collapse_label) {
151
+ var select = $('#' + select_id)[0];
152
+ if (select.multiple == true) {
153
+ select.multiple = false;
154
+ link_obj.title = expand_label;
155
+ } else {
156
+ select.multiple = true;
157
+ link_obj.title = collapse_label;
158
+ }
159
+ }
160
+
161
+ WiceGridProcessor._version = '0.4.3';
@@ -0,0 +1,35 @@
1
+ class WiceGridAssetsJqueryGenerator < Rails::Generator::Base
2
+ def active_js_framework
3
+ 'jquery'
4
+ end
5
+ def inactive_js_framework
6
+ 'prototype'
7
+ end
8
+
9
+ def manifest
10
+ record do |m|
11
+ # wice_grid config
12
+ m.directory "config/initializers"
13
+ @active_js_framework = 'jquery'
14
+ @inactive_js_framework = 'prototype'
15
+ m.template "../../common_templates/initializers/wice_grid_config.rb", "config/initializers/wice_grid_config.rb"
16
+
17
+ # wice_grid locales
18
+ m.directory "config/locales"
19
+ m.file "../../common_templates/locales/wice_grid.yml", "config/locales/wice_grid.yml"
20
+
21
+ # wice_grid js & css
22
+ m.file "javascripts/wice_grid_jquery.js", "public/javascripts/wice_grid.js"
23
+ m.file "../../common_templates/stylesheets/wice_grid.css", "public/stylesheets/wice_grid.css"
24
+
25
+ # images
26
+ m.directory "public/images/icons/grid"
27
+
28
+ %w(arrow_down.gif calendar_view_month.png expand.png page_white_find.png table_refresh.png
29
+ arrow_up.gif delete.png page_white_excel.png table.png tick_all.png untick_all.png ).each do |f|
30
+ m.file "../../common_templates/icons/#{f}", "public/images/icons/grid/#{f}"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies
3
+ configuration file wice_grid_config.rb to config/initializers/
4
+ wice_grid_prototype.js to public/javascripts/wice_grid.js
5
+ wice_grid.css to public/stylesheets/
6
+ calendarview.js to public/javascripts/
7
+ calendarview.css to public/stylesheets/
8
+ wice_grid images to public/images/icons/grid
@@ -0,0 +1,1168 @@
1
+ //
2
+ // CalendarView (for Prototype)
3
+ // calendarview.org
4
+ //
5
+ // Maintained by Justin Mecham <justin@aspect.net>
6
+ //
7
+ // Portions Copyright 2002-2005 Mihai Bazon
8
+ //
9
+ // This calendar is based very loosely on the Dynarch Calendar in that it was
10
+ // used as a base, but completely gutted and more or less rewritten in place
11
+ // to use the Prototype JavaScript library.
12
+ //
13
+ // As such, CalendarView is licensed under the terms of the GNU Lesser General
14
+ // Public License (LGPL). More information on the Dynarch Calendar can be
15
+ // found at:
16
+ //
17
+ // www.dynarch.com/projects/calendar
18
+ //
19
+
20
+
21
+ /* This fork by Yuri Leikind ( git://github.com/leikind/calendarview.git ) adds a number of features.
22
+
23
+ The differences from the original are
24
+
25
+ * Support for time in the form of two dropdowns for hours and minutes. Can be turned off/on.
26
+ * Draggable popup calendars (which introduces new dependancies: script.aculo.us effects.js and dragdrop.js)
27
+ * Close button
28
+ * Ability to unset the date by clicking on the active date
29
+ * Simple I18n support
30
+ * Removed all ambiguity in the API
31
+ * Two strategies in positioning of popup calendars: relative to the popup trigger element (original behavior),
32
+ and is relative to the mouse pointer (can be configured)
33
+ * Popup calendars are not created every time they pop up, on the contrary, they are created once just like
34
+ embedded calendars, and then shown or hidden.
35
+ * Possible to have many popup calendars on page. The behavior of the original calendarview when a popup
36
+ calendar is hidden when the user clicks elsewhere on the page is an option now.
37
+ * Refactoring and changes to the OO design like getting rid of Calendar.prototype in favor of class based
38
+ OO provided by OO, and getting rid of Calendar.setup({}) in favor of a simple object constructor new Calendar({}).
39
+
40
+ */
41
+
42
+ var Calendar = Class.create({
43
+
44
+ container: null,
45
+
46
+ minYear: 1900,
47
+ maxYear: 2100,
48
+
49
+ date: new Date(),
50
+ currentDateElement: null,
51
+
52
+ shouldClose: false,
53
+ isPopup: true,
54
+
55
+ initialize: function(params){
56
+
57
+ if (! Calendar.init_done){
58
+ Calendar.init();
59
+ }
60
+
61
+ embedAt = params.embedAt || null;
62
+ withTime = params.withTime || null;
63
+ dateFormat = params.dateFormat || null;
64
+ initialDate = params.initialDate || null;
65
+ popupTriggerElement = params.popupTriggerElement || null;
66
+ this.onHideCallback = params.onHideCallback || function(date, calendar){};
67
+ this.onDateChangedCallback = params.onDateChangedCallback || function(date, calendar){};
68
+ this.minuteStep = params.minuteStep || 5;
69
+ this.hideOnClickOnDay = params.hideOnClickOnDay || false;
70
+ this.hideOnClickElsewhere = params.hideOnClickElsewhere || false;
71
+ this.outputFields = params.outputFields || $A();
72
+ this.popupPositioningStrategy = params.popupPositioningStrategy || 'trigger'; // or 'pointer'
73
+ this.x = params.x || 0;
74
+ this.y = params.y || 0.6;
75
+
76
+ this.outputFields = $A(this.outputFields).collect(function(f){
77
+ return $(f);
78
+ });
79
+
80
+ if (embedAt){
81
+ this.embedAt = $(embedAt);
82
+ this.embedAt._calendar = this;
83
+ }else{
84
+ this.embedAt = null;
85
+ }
86
+
87
+ this.withTime = withTime;
88
+
89
+ if (dateFormat){
90
+ this.dateFormat = dateFormat;
91
+ }else{
92
+ if(this.withTime){
93
+ this.dateFormat = Calendar.defaultDateTimeFormat;
94
+ }else{
95
+ this.dateFormat = Calendar.defaultDateFormat;
96
+ }
97
+ }
98
+
99
+ this.dateFormatForHiddenField = params.dateFormatForHiddenField || this.dateFormat;
100
+
101
+
102
+ if (initialDate) {
103
+ this.date = this.parseDate(initialDate);
104
+ }
105
+
106
+ this.build();
107
+
108
+ if (this.isPopup) { //Popup Calendars
109
+ var popupTriggerElement = $(popupTriggerElement);
110
+ popupTriggerElement._calendar = this;
111
+
112
+ popupTriggerElement.observe('click', function(event){
113
+ this.showAtElement(event, popupTriggerElement);
114
+ }.bind(this) );
115
+
116
+ } else{ // In-Page Calendar
117
+ this.show();
118
+ }
119
+
120
+ if (params.updateOuterFieldsOnInit){
121
+ this.updateOuterFieldWithoutCallback(); // Just for the sake of localization and DatePicker
122
+ }
123
+ },
124
+
125
+ build: function(){
126
+ if (this.embedAt) {
127
+ var parentForCalendarTable = this.embedAt;
128
+ this.isPopup = false;
129
+ } else {
130
+ var parentForCalendarTable = document.getElementsByTagName('body')[0];
131
+ this.isPopup = true;
132
+ }
133
+
134
+
135
+ var table = new Element('table');
136
+
137
+ var thead = new Element('thead');
138
+ table.appendChild(thead);
139
+
140
+ var firstRow = new Element('tr');
141
+
142
+ if (this.isPopup){
143
+ var cell = new Element('td');
144
+ cell.addClassName('draggableHandler');
145
+ firstRow.appendChild(cell);
146
+
147
+ cell = new Element('td', { colSpan: 5 });
148
+ cell.addClassName('title' );
149
+ cell.addClassName('draggableHandler');
150
+ firstRow.appendChild(cell);
151
+
152
+ cell = new Element('td');
153
+ cell.addClassName('closeButton');
154
+ firstRow.appendChild(cell);
155
+ cell.update('x');
156
+
157
+ cell.observe('mousedown', function(){
158
+ this.hide();
159
+ }.bind(this));
160
+
161
+
162
+ }else{
163
+ var cell = new Element('td', { colSpan: 7 } );
164
+ firstRow.appendChild(cell);
165
+ }
166
+
167
+ cell.addClassName('title');
168
+
169
+ thead.appendChild(firstRow);
170
+
171
+ var row = new Element('tr')
172
+ this._drawButtonCell(row, '&#x00ab;', 1, Calendar.NAV_PREVIOUS_YEAR);
173
+ this._drawButtonCell(row, '&#x2039;', 1, Calendar.NAV_PREVIOUS_MONTH);
174
+ this._drawButtonCell(row, Calendar.getMessageFor('today'), 3, Calendar.NAV_TODAY);
175
+ this._drawButtonCell(row, '&#x203a;', 1, Calendar.NAV_NEXT_MONTH);
176
+ this._drawButtonCell(row, '&#x00bb;', 1, Calendar.NAV_NEXT_YEAR);
177
+ thead.appendChild(row)
178
+
179
+ // Day Names
180
+ row = new Element('tr');
181
+ for (var i = 0; i < 7; ++i) {
182
+ cell = new Element('th').update(Calendar.SHORT_DAY_NAMES[i]);
183
+ if (i == 0 || i == 6){
184
+ cell.addClassName('weekend');
185
+ }
186
+ row.appendChild(cell);
187
+ }
188
+ thead.appendChild(row);
189
+
190
+ // Calendar Days
191
+ var tbody = table.appendChild(new Element('tbody'));
192
+ for (i = 6; i > 0; --i) {
193
+ row = tbody.appendChild(new Element('tr'));
194
+ row.addClassName('days');
195
+ for (var j = 7; j > 0; --j) {
196
+ cell = row.appendChild(new Element('td'));
197
+ cell.calendar = this;
198
+ }
199
+ }
200
+
201
+ // Time Placeholder
202
+ if (this.withTime){
203
+ var tfoot = table.appendChild(new Element('tfoot'));
204
+ row = tfoot.appendChild(new Element('tr'));
205
+ cell = row.appendChild(new Element('td', { colSpan: 7 }));
206
+ cell.addClassName('time');
207
+ var hourSelect = cell.appendChild(new Element('select', { name : 'hourSelect'}));
208
+ for (var i = 0; i < 24; i++) {
209
+ hourSelect.appendChild(new Element('option', {value : i}).update(i));
210
+ }
211
+ this.hourSelect = hourSelect;
212
+
213
+ cell.appendChild(new Element('span')).update(' : ');
214
+
215
+ var minuteSelect = cell.appendChild(new Element('select', { name : 'minuteSelect'}));
216
+ for (var i = 0; i < 60; i += this.minuteStep) {
217
+ minuteSelect.appendChild(new Element('option', {value : i}).update(i));
218
+ }
219
+ this.minuteSelect = minuteSelect;
220
+
221
+ hourSelect.observe('change', function(event){
222
+ if (! this.date) return;
223
+ var elem = event.element();
224
+ var selectedIndex = elem.selectedIndex;
225
+ if ((typeof selectedIndex != 'undefined') && selectedIndex != null){
226
+ this.date.setHours(elem.options[selectedIndex].value);
227
+ this.updateOuterField();
228
+ }
229
+ }.bind(this));
230
+
231
+ minuteSelect.observe('change', function(event){
232
+ if (! this.date) return;
233
+ var elem = event.element();
234
+ var selectedIndex = elem.selectedIndex;
235
+ if ((typeof selectedIndex != 'undefined') && selectedIndex != null){
236
+ this.date.setMinutes(elem.options[selectedIndex].value);
237
+ this.updateOuterField();
238
+ }
239
+ }.bind(this))
240
+ }
241
+
242
+ // Calendar Container (div)
243
+ this.container = new Element('div');
244
+ this.container.addClassName('calendar');
245
+ if (this.isPopup) {
246
+ this.container.setStyle({ position: 'absolute', display: 'none' });
247
+ this.container.addClassName('popup');
248
+ }
249
+ this.container.appendChild(table);
250
+
251
+ this.update(this.date);
252
+
253
+ Event.observe(this.container, 'mousedown', Calendar.handleMouseDownEvent);
254
+
255
+ parentForCalendarTable.appendChild(this.container);
256
+
257
+ if (this.isPopup){
258
+ new Draggable(table, {handle : firstRow });
259
+ }
260
+ },
261
+
262
+ updateOuterFieldReal: function(element){
263
+ if (element.tagName == 'DIV' || element.tagName == 'SPAN') {
264
+ formatted = this.date ? this.date.print(this.dateFormat) : '';
265
+ element.update(formatted);
266
+ } else if (element.tagName == 'INPUT') {
267
+ formatted = this.date ? this.date.print(this.dateFormatForHiddenField) : '';
268
+ element.value = formatted;
269
+ }
270
+ },
271
+
272
+ updateOuterFieldWithoutCallback: function(){
273
+ this.outputFields.each(function(field){
274
+ this.updateOuterFieldReal(field);
275
+ }.bind(this));
276
+ },
277
+
278
+ updateOuterField: function(){
279
+ this.updateOuterFieldWithoutCallback();
280
+ this.onDateChangedCallback(this.date, this);
281
+ },
282
+
283
+ viewOutputFields: function(){
284
+ return this.outputFields.findAll(function(element){
285
+ if (element.tagName == 'DIV' || element.tagName == 'SPAN'){
286
+ return true;
287
+ }else{
288
+ return false;
289
+ }
290
+ });
291
+ },
292
+
293
+
294
+ //----------------------------------------------------------------------------
295
+ // Update Calendar
296
+ //----------------------------------------------------------------------------
297
+
298
+ update: function(date) {
299
+
300
+ var today = new Date();
301
+ var thisYear = today.getFullYear();
302
+ var thisMonth = today.getMonth();
303
+ var thisDay = today.getDate();
304
+ var month = date.getMonth();
305
+ var dayOfMonth = date.getDate();
306
+ var hour = date.getHours();
307
+ var minute = date.getMinutes();
308
+
309
+ // Ensure date is within the defined range
310
+ if (date.getFullYear() < this.minYear)
311
+ date.__setFullYear(this.minYear);
312
+ else if (date.getFullYear() > this.maxYear)
313
+ date.__setFullYear(this.maxYear);
314
+
315
+ if (this.isBackedUp()){
316
+ this.dateBackedUp = new Date(date);
317
+ }else{
318
+ this.date = new Date(date);
319
+ }
320
+
321
+ // Calculate the first day to display (including the previous month)
322
+ date.setDate(1)
323
+ date.setDate(-(date.getDay()) + 1)
324
+
325
+ // Fill in the days of the month
326
+ Element.getElementsBySelector(this.container, 'tbody tr').each(
327
+ function(row, i) {
328
+ var rowHasDays = false;
329
+ row.immediateDescendants().each(
330
+ function(cell, j) {
331
+ var day = date.getDate();
332
+ var dayOfWeek = date.getDay();
333
+ var isCurrentMonth = (date.getMonth() == month);
334
+
335
+ // Reset classes on the cell
336
+ cell.className = '';
337
+ cell.date = new Date(date);
338
+ cell.update(day);
339
+
340
+ // Account for days of the month other than the current month
341
+ if (!isCurrentMonth)
342
+ cell.addClassName('otherDay');
343
+ else
344
+ rowHasDays = true;
345
+
346
+ // Ensure the current day is selected
347
+ if ((! this.isBackedUp()) && isCurrentMonth && day == dayOfMonth) {
348
+ cell.addClassName('selected');
349
+ this.currentDateElement = cell;
350
+ }
351
+
352
+ // Today
353
+ if (date.getFullYear() == thisYear && date.getMonth() == thisMonth && day == thisDay)
354
+ cell.addClassName('today');
355
+
356
+ // Weekend
357
+ if ([0, 6].indexOf(dayOfWeek) != -1)
358
+ cell.addClassName('weekend');
359
+
360
+ // Set the date to tommorrow
361
+ date.setDate(day + 1);
362
+ }.bind(this)
363
+ )
364
+ // Hide the extra row if it contains only days from another month
365
+ !rowHasDays ? row.hide() : row.show();
366
+ }.bind(this)
367
+ )
368
+
369
+ Element.getElementsBySelector(this.container, 'tfoot tr td select').each(
370
+ function(sel){
371
+ if(sel.name == 'hourSelect'){
372
+ sel.selectedIndex = hour;
373
+ }else if(sel.name == 'minuteSelect'){
374
+ if (this.minuteStep == 1){
375
+ sel.selectedIndex = minute;
376
+ }else{
377
+ sel.selectedIndex = this.findClosestMinute(minute);
378
+ }
379
+ }
380
+ }.bind(this)
381
+ )
382
+
383
+ this.container.getElementsBySelector('td.title')[0].update(
384
+ Calendar.MONTH_NAMES[month] + ' ' + this.dateOrDateBackedUp().getFullYear()
385
+ )
386
+
387
+ },
388
+
389
+
390
+ findClosestMinute: function(val){
391
+ if (val == 0){
392
+ return 0;
393
+ }
394
+ var lowest = ((val / this.minuteStep).floor() * this.minuteStep);
395
+ var distance = val % this.minuteStep;
396
+ var minuteValueToShow;
397
+
398
+ if (distance <= (this.minuteStep / 2)){
399
+ minuteValueToShow = lowest;
400
+ }else{
401
+ minuteValueToShow = lowest + this.minuteStep;
402
+ }
403
+
404
+ if (minuteValueToShow == 0){
405
+ return minuteValueToShow;
406
+ }else if(minuteValueToShow >= 60){
407
+ return (minuteValueToShow / this.minuteStep).floor() - 1;
408
+ }else{
409
+ return minuteValueToShow / this.minuteStep;
410
+ }
411
+ },
412
+
413
+ _drawButtonCell: function(parentForCell, text, colSpan, navAction) {
414
+ var cell = new Element('td');
415
+ if (colSpan > 1) cell.colSpan = colSpan;
416
+ cell.className = 'cvbutton';
417
+ cell.calendar = this;
418
+ cell.navAction = navAction;
419
+ cell.innerHTML = text;
420
+ cell.unselectable = 'on'; // IE
421
+ parentForCell.appendChild(cell);
422
+ return cell;
423
+ },
424
+
425
+
426
+ //------------------------------------------------------------------------------
427
+ // Calendar Display Functions
428
+ //------------------------------------------------------------------------------
429
+
430
+ show: function(){
431
+ this.container.show();
432
+ if (this.isPopup) {
433
+ if (this.hideOnClickElsewhere){
434
+ window._popupCalendar = this;
435
+ document.observe('mousedown', Calendar._checkCalendar);
436
+ }
437
+ }
438
+ },
439
+
440
+ showAt: function (x, y) {
441
+ this.container.setStyle({ left: x + 'px', top: y + 'px' });
442
+ this.show();
443
+ },
444
+
445
+
446
+ showAtElement: function(event, element) {
447
+ this.container.show();
448
+ var x, y;
449
+ if (this.popupPositioningStrategy == 'pointer'){ // follow the mouse pointer
450
+ var pos = Event.pointer(event);
451
+ var containerWidth = this.container.getWidth();
452
+ x = containerWidth * this.x + pos.x;
453
+ y = containerWidth * this.y + pos.y;
454
+ }else{ // 'container' - container of the trigger elements
455
+ var pos = Position.cumulativeOffset(element);
456
+ x = pos[0];
457
+ y = this.container.offsetHeight * 0.75 + pos[1];
458
+ }
459
+ this.showAt(x, y);
460
+ },
461
+
462
+ hide: function() {
463
+ if (this.isPopup){
464
+ Event.stopObserving(document, 'mousedown', Calendar._checkCalendar);
465
+ }
466
+ this.container.hide();
467
+ this.onHideCallback(this.date, this);
468
+ },
469
+
470
+
471
+ // Tries to identify the date represented in a string. If successful it also
472
+ // calls this.updateIfDateDifferent which moves the calendar to the given date.
473
+ parseDate: function(str, format){
474
+ if (!format){
475
+ format = this.dateFormat;
476
+ }
477
+ var res = Date.parseDate(str, format);
478
+ return res;
479
+ },
480
+
481
+
482
+ dateOrDateBackedUp: function(){
483
+ return this.date || this.dateBackedUp;
484
+ },
485
+
486
+ updateIfDateDifferent: function(date) {
487
+ if (!date.equalsTo(this.dateOrDateBackedUp())){
488
+ this.update(date);
489
+ }
490
+ },
491
+
492
+ backupDateAndCurrentElement: function(){
493
+ if (this.minuteSelect){
494
+ this.minuteSelect.disable();
495
+ }
496
+ if (this.hourSelect){
497
+ this.hourSelect.disable();
498
+ }
499
+
500
+ this.currentDateElementBackedUp = this.currentDateElement;
501
+ this.currentDateElement = null;
502
+
503
+ this.dateBackedUp = this.date;
504
+ this.date = null;
505
+ },
506
+
507
+ restoreDateAndCurrentElement: function(){
508
+ if (this.minuteSelect){
509
+ this.minuteSelect.enable();
510
+ }
511
+ if (this.hourSelect){
512
+ this.hourSelect.enable();
513
+ }
514
+
515
+ this.currentDateElement = this.currentDateElementBackedUp;
516
+ this.currentDateElementBackedUp = null;
517
+
518
+ this.date = this.dateBackedUp;
519
+ this.dateBackedUp = null;
520
+ },
521
+
522
+ isBackedUp: function(){
523
+ return ((this.date == null) && this.dateBackedUp);
524
+ },
525
+
526
+ dumpDates: function(){
527
+ console.log('date: ' + this.date);
528
+ console.log('dateBackedUp: ' + this.dateBackedUp);
529
+ },
530
+
531
+
532
+ setRange: function(minYear, maxYear) {
533
+ this.minYear = minYear;
534
+ this.maxYear = maxYear;
535
+ }
536
+ })
537
+
538
+ // Delete or add new locales from I18n.js according to your needs
539
+ Calendar.messagebundle = $H({'en' :
540
+ $H({
541
+ 'monday' : 'Monday',
542
+ 'tuesday' : 'Tuesday',
543
+ 'wednesday' : 'Wednesday',
544
+ 'thursday' : 'Thursday',
545
+ 'friday' : 'Friday',
546
+ 'saturday' : 'Saturday',
547
+ 'sunday' : 'Sunday',
548
+
549
+ 'monday_short' : 'M',
550
+ 'tuesday_short' : 'T',
551
+ 'wednesday_short' : 'W',
552
+ 'thursday_short' : 'T',
553
+ 'friday_short' : 'F',
554
+ 'saturday_short' : 'S',
555
+ 'sunday_short' : 'S',
556
+
557
+ 'january' : 'January',
558
+ 'february' : 'February',
559
+ 'march' : 'March',
560
+ 'april' : 'April',
561
+ 'may' : 'May',
562
+ 'june' : 'June',
563
+ 'july' : 'July',
564
+ 'august' : 'August',
565
+ 'september' : 'September',
566
+ 'october' : 'October',
567
+ 'november' : 'November',
568
+ 'december' : 'December',
569
+
570
+ 'january_short' : 'Jan',
571
+ 'february_short' : 'Feb',
572
+ 'march_short' : 'Mar',
573
+ 'april_short' : 'Apr',
574
+ 'may_short' : 'May',
575
+ 'june_short' : 'Jun',
576
+ 'july_short' : 'Jul',
577
+ 'august_short' : 'Aug',
578
+ 'september_short' : 'Sep',
579
+ 'october_short' : 'Oct',
580
+ 'november_short' : 'Nov',
581
+ 'december_short' : 'Dec',
582
+
583
+ 'today' : 'Today'
584
+ }),
585
+ 'fr' :
586
+ $H({
587
+ 'monday' : 'Lundi',
588
+ 'tuesday' : 'Mardi',
589
+ 'wednesday' : 'Mercredi',
590
+ 'thursday' : 'Jeudi',
591
+ 'friday' : 'Vendredi',
592
+ 'saturday' : 'Samedi',
593
+ 'sunday' : 'Dimanche',
594
+
595
+ 'monday_short' : 'Lu',
596
+ 'tuesday_short' : 'Ma',
597
+ 'wednesday_short' : 'Me',
598
+ 'thursday_short' : 'Je',
599
+ 'friday_short' : 'Ve',
600
+ 'saturday_short' : 'Sa',
601
+ 'sunday_short' : 'Di',
602
+
603
+ 'january' : 'janvier',
604
+ 'february' : 'février',
605
+ 'march' : 'mars',
606
+ 'april' : 'avril',
607
+ 'may' : 'mai',
608
+ 'june' : 'juin',
609
+ 'july' : 'juillet',
610
+ 'august' : 'août',
611
+ 'september' : 'septembre',
612
+ 'october' : 'octobre',
613
+ 'november' : 'novembre',
614
+ 'december' : 'décembre',
615
+
616
+ 'january_short' : 'jan',
617
+ 'february_short' : 'fév',
618
+ 'march_short' : 'mar',
619
+ 'april_short' : 'avr',
620
+ 'may_short' : 'mai',
621
+ 'june_short' : 'jun',
622
+ 'july_short' : 'jul',
623
+ 'august_short' : 'aoû',
624
+ 'september_short' : 'sep',
625
+ 'october_short' : 'oct',
626
+ 'november_short' : 'nov',
627
+ 'december_short' : 'dec',
628
+
629
+ 'today' : 'aujourd\'hui'
630
+ }),
631
+ 'nl' :
632
+ $H({
633
+ 'monday' : 'maandag',
634
+ 'tuesday' : 'dinsdag',
635
+ 'wednesday' : 'woensdag',
636
+ 'thursday' : 'donderdag',
637
+ 'friday' : 'vrijdag',
638
+ 'saturday' : 'zaterdag',
639
+ 'sunday' : 'zondag',
640
+
641
+ 'monday_short' : 'Ma',
642
+ 'tuesday_short' : 'Di',
643
+ 'wednesday_short' : 'Wo',
644
+ 'thursday_short' : 'Do',
645
+ 'friday_short' : 'Vr',
646
+ 'saturday_short' : 'Za',
647
+ 'sunday_short' : 'Zo',
648
+
649
+ 'january' : 'januari',
650
+ 'february' : 'februari',
651
+ 'march' : 'maart',
652
+ 'april' : 'april',
653
+ 'may' : 'mei',
654
+ 'june' : 'juni',
655
+ 'july' : 'juli',
656
+ 'august' : 'augustus',
657
+ 'september' : 'september',
658
+ 'october' : 'oktober',
659
+ 'november' : 'november',
660
+ 'december' : 'december',
661
+
662
+ 'january_short' : 'jan',
663
+ 'february_short' : 'feb',
664
+ 'march_short' : 'mrt',
665
+ 'april_short' : 'apr',
666
+ 'may_short' : 'mei',
667
+ 'june_short' : 'jun',
668
+ 'july_short' : 'jul',
669
+ 'august_short' : 'aug',
670
+ 'september_short' : 'sep',
671
+ 'october_short' : 'okt',
672
+ 'november_short' : 'nov',
673
+ 'december_short' : 'dec',
674
+
675
+ 'today' : 'vandaag'
676
+ })
677
+ });
678
+
679
+
680
+ Calendar.getMessageFor = function(key){
681
+
682
+ var lang = Calendar.language || 'en';
683
+ if (! Calendar.messagebundle.get(lang)){
684
+ lang = 'en';
685
+ }
686
+ return Calendar.messagebundle.get(lang).get(key);
687
+ };
688
+
689
+ Calendar.VERSION = '1.4';
690
+
691
+ Calendar.defaultDateFormat = '%Y-%m-%d';
692
+ Calendar.defaultDateTimeFormat = '%Y-%m-%d %H:%M';
693
+
694
+ // we need to postpone the initialization of these structures to let the page define the language of the page
695
+ Calendar.init = function(){
696
+
697
+ Calendar.DAY_NAMES = new Array(
698
+ Calendar.getMessageFor('sunday'),
699
+ Calendar.getMessageFor('monday'),
700
+ Calendar.getMessageFor('tuesday'),
701
+ Calendar.getMessageFor('wednesday'),
702
+ Calendar.getMessageFor('thursday'),
703
+ Calendar.getMessageFor('friday'),
704
+ Calendar.getMessageFor('saturday')
705
+ );
706
+
707
+ Calendar.SHORT_DAY_NAMES = new Array(
708
+ Calendar.getMessageFor('sunday_short'),
709
+ Calendar.getMessageFor('monday_short'),
710
+ Calendar.getMessageFor('tuesday_short'),
711
+ Calendar.getMessageFor('wednesday_short'),
712
+ Calendar.getMessageFor('thursday_short'),
713
+ Calendar.getMessageFor('friday_short'),
714
+ Calendar.getMessageFor('saturday_short')
715
+ );
716
+
717
+ Calendar.MONTH_NAMES = new Array(
718
+ Calendar.getMessageFor('january'),
719
+ Calendar.getMessageFor('february'),
720
+ Calendar.getMessageFor('march'),
721
+ Calendar.getMessageFor('april'),
722
+ Calendar.getMessageFor('may'),
723
+ Calendar.getMessageFor('june'),
724
+ Calendar.getMessageFor('july'),
725
+ Calendar.getMessageFor('august'),
726
+ Calendar.getMessageFor('september'),
727
+ Calendar.getMessageFor('october'),
728
+ Calendar.getMessageFor('november'),
729
+ Calendar.getMessageFor('december')
730
+ );
731
+
732
+ Calendar.SHORT_MONTH_NAMES = new Array(
733
+ Calendar.getMessageFor('january_short'),
734
+ Calendar.getMessageFor('february_short'),
735
+ Calendar.getMessageFor('march_short'),
736
+ Calendar.getMessageFor('april_short'),
737
+ Calendar.getMessageFor('may_short'),
738
+ Calendar.getMessageFor('june_short'),
739
+ Calendar.getMessageFor('july_short'),
740
+ Calendar.getMessageFor('august_short'),
741
+ Calendar.getMessageFor('september_short'),
742
+ Calendar.getMessageFor('october_short'),
743
+ Calendar.getMessageFor('november_short'),
744
+ Calendar.getMessageFor('december_short')
745
+ );
746
+ Calendar.init_done = true;
747
+ };
748
+
749
+ Calendar.NAV_PREVIOUS_YEAR = -2;
750
+ Calendar.NAV_PREVIOUS_MONTH = -1;
751
+ Calendar.NAV_TODAY = 0;
752
+ Calendar.NAV_NEXT_MONTH = 1;
753
+ Calendar.NAV_NEXT_YEAR = 2;
754
+
755
+ //------------------------------------------------------------------------------
756
+ // Static Methods
757
+ //------------------------------------------------------------------------------
758
+
759
+ // This gets called when the user presses a mouse button anywhere in the
760
+ // document, if the calendar is shown. If the click was outside the open
761
+ // calendar this function closes it.
762
+ Calendar._checkCalendar = function(event) {
763
+ if (!window._popupCalendar){
764
+ return false;
765
+ }
766
+ if (Element.descendantOf(Event.element(event), window._popupCalendar.container)){
767
+ return;
768
+ }
769
+ Calendar.closeHandler(window._popupCalendar);
770
+ return Event.stop(event);
771
+ }
772
+
773
+ //------------------------------------------------------------------------------
774
+ // Event Handlers
775
+ //------------------------------------------------------------------------------
776
+
777
+ Calendar.handleMouseDownEvent = function(event){
778
+ if (event.element().type == 'select-one'){ // ignore select elements - not escaping this in Safari leaves select boxes non-functional
779
+ return true;
780
+ }
781
+ Event.observe(document, 'mouseup', Calendar.handleMouseUpEvent);
782
+ Event.stop(event)
783
+ }
784
+
785
+ Calendar.handleMouseUpEvent = function(event){
786
+ var el = Event.element(event);
787
+ var calendar = el.calendar;
788
+ var isNewDate = false;
789
+
790
+
791
+ // If the element that was clicked on does not have an associated Calendar
792
+ // object, return as we have nothing to do.
793
+ if (!calendar) return false;
794
+
795
+ // Clicked on a day
796
+ if (typeof el.navAction == 'undefined') {
797
+
798
+ var dateWasDefined = true;
799
+ if (calendar.date == null){
800
+ dateWasDefined = false;
801
+ calendar.restoreDateAndCurrentElement();
802
+ }
803
+
804
+
805
+ if (calendar.currentDateElement) {
806
+ Element.removeClassName(calendar.currentDateElement, 'selected');
807
+
808
+ if (dateWasDefined && el == calendar.currentDateElement){
809
+ calendar.backupDateAndCurrentElement();
810
+
811
+ calendar.updateOuterField();
812
+
813
+ Event.stopObserving(document, 'mouseup', Calendar.handleMouseUpEvent);
814
+ return Event.stop(event);
815
+ }
816
+
817
+ Element.addClassName(el, 'selected');
818
+
819
+ calendar.shouldClose = (calendar.currentDateElement == el);
820
+
821
+ if (!calendar.shouldClose) {
822
+
823
+ calendar.currentDateElement = el;
824
+ }
825
+ }
826
+ calendar.date.setDateOnly(el.date);
827
+ isNewDate = true;
828
+
829
+ calendar.shouldClose = !el.hasClassName('otherDay');
830
+
831
+
832
+ var isOtherMonth = !calendar.shouldClose;
833
+ if (isOtherMonth) {
834
+ calendar.update(calendar.date);
835
+ }
836
+
837
+ if (! calendar.hideOnClickOnDay){ // override closing if calendar.hideOnClickOnDay is false
838
+ calendar.shouldClose = false;
839
+ }
840
+
841
+ } else { // Clicked on an action button
842
+
843
+ var date = new Date(calendar.dateOrDateBackedUp());
844
+
845
+ if (el.navAction == Calendar.NAV_TODAY){
846
+ date.setDateOnly(new Date());
847
+ }
848
+
849
+ var year = date.getFullYear();
850
+ var mon = date.getMonth();
851
+
852
+ function setMonth(m) {
853
+ var day = date.getDate();
854
+ var max = date.getMonthDays(m);
855
+ if (day > max) date.setDate(max);
856
+ date.setMonth(m);
857
+ }
858
+
859
+ switch (el.navAction) {
860
+
861
+ // Previous Year
862
+ case Calendar.NAV_PREVIOUS_YEAR:
863
+ if (year > calendar.minYear)
864
+ date.__setFullYear(year - 1);
865
+ break;
866
+
867
+ // Previous Month
868
+ case Calendar.NAV_PREVIOUS_MONTH:
869
+ if (mon > 0) {
870
+ setMonth(mon - 1);
871
+ }
872
+ else if (year-- > calendar.minYear) {
873
+ date.__setFullYear(year);
874
+ setMonth(11);
875
+ }
876
+ break;
877
+
878
+ // Today
879
+ case Calendar.NAV_TODAY:
880
+ break;
881
+
882
+ // Next Month
883
+ case Calendar.NAV_NEXT_MONTH:
884
+ if (mon < 11) {
885
+ setMonth(mon + 1);
886
+ }else if (year < calendar.maxYear) {
887
+ date.__setFullYear(year + 1);
888
+ setMonth(0);
889
+ }
890
+ break;
891
+
892
+ // Next Year
893
+ case Calendar.NAV_NEXT_YEAR:
894
+ if (year < calendar.maxYear){
895
+ date.__setFullYear(year + 1);
896
+ }
897
+ break;
898
+ }
899
+
900
+ if (!date.equalsTo(calendar.dateOrDateBackedUp())) {
901
+ calendar.updateIfDateDifferent(date);
902
+ isNewDate = true;
903
+ } // else if (el.navAction == 0) {
904
+ // isNewDate = (calendar.shouldClose = true);
905
+ // } // Hm, what did I mean with this code?
906
+ }
907
+
908
+ if (isNewDate && event) {
909
+ Calendar.selectHandler(calendar);
910
+ }
911
+
912
+ if (calendar.shouldClose && event) {
913
+ Calendar.closeHandler(calendar);
914
+ }
915
+
916
+ Event.stopObserving(document, 'mouseup', Calendar.handleMouseUpEvent);
917
+ return Event.stop(event);
918
+ }
919
+
920
+ Calendar.selectHandler = function(calendar){
921
+
922
+ // Update dateField value
923
+ calendar.updateOuterField();
924
+
925
+
926
+ // Call the close handler, if necessary
927
+ if (calendar.shouldClose) {
928
+ Calendar.closeHandler(calendar);
929
+ }
930
+ }
931
+
932
+ Calendar.closeHandler = function(calendar){
933
+ calendar.hide();
934
+ calendar.shouldClose = false;
935
+ }
936
+
937
+
938
+
939
+ // global object that remembers the calendar
940
+ window._popupCalendar = null;
941
+
942
+
943
+ //==============================================================================
944
+ //
945
+ // Date Object Patches
946
+ //
947
+ // This is pretty much untouched from the original. I really would like to get
948
+ // rid of these patches if at all possible and find a cleaner way of
949
+ // accomplishing the same things. It's a shame Prototype doesn't extend Date at
950
+ // all.
951
+ //
952
+ //==============================================================================
953
+
954
+ Date.DAYS_IN_MONTH = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
955
+ Date.SECOND = 1000 /* milliseconds */
956
+ Date.MINUTE = 60 * Date.SECOND
957
+ Date.HOUR = 60 * Date.MINUTE
958
+ Date.DAY = 24 * Date.HOUR
959
+ Date.WEEK = 7 * Date.DAY
960
+
961
+ // Parses Date
962
+ Date.parseDate = function(str, fmt) {
963
+ if (str){
964
+ str = new String(str);
965
+ }else{
966
+ str = new String('');
967
+ }
968
+ str = str.strip();
969
+
970
+ var today = new Date();
971
+ var y = 0;
972
+ var m = -1;
973
+ var d = 0;
974
+ var a = str.split(/\W+/);
975
+ var b = fmt.match(/%./g);
976
+ var i = 0, j = 0;
977
+ var hr = 0;
978
+ var min = 0;
979
+
980
+ for (i = 0; i < a.length; ++i) {
981
+ if (!a[i]) continue;
982
+ switch (b[i]) {
983
+ case "%d":
984
+ case "%e":
985
+ d = parseInt(a[i], 10);
986
+ break;
987
+ case "%m":
988
+ m = parseInt(a[i], 10) - 1;
989
+ break;
990
+ case "%Y":
991
+ case "%y":
992
+ y = parseInt(a[i], 10);
993
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
994
+ break;
995
+ case "%b":
996
+ case "%B":
997
+ for (j = 0; j < 12; ++j) {
998
+ if (Calendar.MONTH_NAMES[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) {
999
+ m = j;
1000
+ break;
1001
+ }
1002
+ }
1003
+ break;
1004
+ case "%H":
1005
+ case "%I":
1006
+ case "%k":
1007
+ case "%l":
1008
+ hr = parseInt(a[i], 10);
1009
+ break;
1010
+ case "%P":
1011
+ case "%p":
1012
+ if (/pm/i.test(a[i]) && hr < 12)
1013
+ hr += 12;
1014
+ else if (/am/i.test(a[i]) && hr >= 12)
1015
+ hr -= 12;
1016
+ break;
1017
+ case "%M":
1018
+ min = parseInt(a[i], 10);
1019
+ break;
1020
+ }
1021
+ }
1022
+ if (isNaN(y)) y = today.getFullYear();
1023
+ if (isNaN(m)) m = today.getMonth();
1024
+ if (isNaN(d)) d = today.getDate();
1025
+ if (isNaN(hr)) hr = today.getHours();
1026
+ if (isNaN(min)) min = today.getMinutes();
1027
+ if (y != 0 && m != -1 && d != 0)
1028
+ return new Date(y, m, d, hr, min, 0);
1029
+ y = 0; m = -1; d = 0;
1030
+ for (i = 0; i < a.length; ++i) {
1031
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
1032
+ var t = -1;
1033
+ for (j = 0; j < 12; ++j) {
1034
+ if (Calendar.MONTH_NAMES[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
1035
+ }
1036
+ if (t != -1) {
1037
+ if (m != -1) {
1038
+ d = m+1;
1039
+ }
1040
+ m = t;
1041
+ }
1042
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
1043
+ m = a[i]-1;
1044
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
1045
+ y = parseInt(a[i], 10);
1046
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
1047
+ } else if (d == 0) {
1048
+ d = a[i];
1049
+ }
1050
+ }
1051
+ if (y == 0)
1052
+ y = today.getFullYear();
1053
+ if (m != -1 && d != 0)
1054
+ return new Date(y, m, d, hr, min, 0);
1055
+ return today;
1056
+ };
1057
+
1058
+ // Returns the number of days in the current month
1059
+ Date.prototype.getMonthDays = function(month) {
1060
+ var year = this.getFullYear()
1061
+ if (typeof month == "undefined")
1062
+ month = this.getMonth()
1063
+ if (((0 == (year % 4)) && ( (0 != (year % 100)) || (0 == (year % 400)))) && month == 1)
1064
+ return 29
1065
+ else
1066
+ return Date.DAYS_IN_MONTH[month]
1067
+ };
1068
+
1069
+ // Returns the number of day in the year
1070
+ Date.prototype.getDayOfYear = function() {
1071
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
1072
+ var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
1073
+ var time = now - then;
1074
+ return Math.floor(time / Date.DAY);
1075
+ };
1076
+
1077
+ /** Returns the number of the week in year, as defined in ISO 8601. */
1078
+ Date.prototype.getWeekNumber = function() {
1079
+ var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
1080
+ var DoW = d.getDay();
1081
+ d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
1082
+ var ms = d.valueOf(); // GMT
1083
+ d.setMonth(0);
1084
+ d.setDate(4); // Thu in Week 1
1085
+ return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
1086
+ };
1087
+
1088
+ /** Checks date and time equality */
1089
+ Date.prototype.equalsTo = function(date) {
1090
+ return ((this.getFullYear() == date.getFullYear()) &&
1091
+ (this.getMonth() == date.getMonth()) &&
1092
+ (this.getDate() == date.getDate()) &&
1093
+ (this.getHours() == date.getHours()) &&
1094
+ (this.getMinutes() == date.getMinutes()));
1095
+ };
1096
+
1097
+ /** Set only the year, month, date parts (keep existing time) */
1098
+ Date.prototype.setDateOnly = function(date) {
1099
+ var tmp = new Date(date);
1100
+ this.setDate(1);
1101
+ this.__setFullYear(tmp.getFullYear());
1102
+ this.setMonth(tmp.getMonth());
1103
+ this.setDate(tmp.getDate());
1104
+ };
1105
+
1106
+ /** Prints the date in a string according to the given format. */
1107
+ Date.prototype.print = function (str) {
1108
+ var m = this.getMonth();
1109
+ var d = this.getDate();
1110
+ var y = this.getFullYear();
1111
+ var wn = this.getWeekNumber();
1112
+ var w = this.getDay();
1113
+ var s = {};
1114
+ var hr = this.getHours();
1115
+ var pm = (hr >= 12);
1116
+ var ir = (pm) ? (hr - 12) : hr;
1117
+ var dy = this.getDayOfYear();
1118
+ if (ir == 0)
1119
+ ir = 12;
1120
+ var min = this.getMinutes();
1121
+ var sec = this.getSeconds();
1122
+ s["%a"] = Calendar.SHORT_DAY_NAMES[w]; // abbreviated weekday name [FIXME: I18N]
1123
+ s["%A"] = Calendar.DAY_NAMES[w]; // full weekday name
1124
+ s["%b"] = Calendar.SHORT_MONTH_NAMES[m]; // abbreviated month name [FIXME: I18N]
1125
+ s["%B"] = Calendar.MONTH_NAMES[m]; // full month name
1126
+ // FIXME: %c : preferred date and time representation for the current locale
1127
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
1128
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
1129
+ s["%e"] = d; // the day of the month (range 1 to 31)
1130
+ // FIXME: %D : american date style: %m/%d/%y
1131
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
1132
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
1133
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
1134
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
1135
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
1136
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
1137
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
1138
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
1139
+ s["%n"] = "\n"; // a newline character
1140
+ s["%p"] = pm ? "PM" : "AM";
1141
+ s["%P"] = pm ? "pm" : "am";
1142
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
1143
+ // FIXME: %R : the time in 24-hour notation %H:%M
1144
+ s["%s"] = Math.floor(this.getTime() / 1000);
1145
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
1146
+ s["%t"] = "\t"; // a tab character
1147
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
1148
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
1149
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
1150
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
1151
+ // FIXME: %x : preferred date representation for the current locale without the time
1152
+ // FIXME: %X : preferred time representation for the current locale without the date
1153
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
1154
+ s["%Y"] = y; // year with the century
1155
+ s["%%"] = "%"; // a literal '%' character
1156
+
1157
+ return str.gsub(/%./, function(match) { return s[match] || match });
1158
+ };
1159
+
1160
+
1161
+ Date.prototype.__setFullYear = function(y) {
1162
+ var d = new Date(this);
1163
+ d.setFullYear(y);
1164
+ if (d.getMonth() != this.getMonth())
1165
+ this.setDate(28);
1166
+ this.setFullYear(y);
1167
+ };
1168
+