stuff_to_do_plugin 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/COPYRIGHT.txt +18 -0
  2. data/CREDITS.txt +6 -0
  3. data/GPL.txt +339 -0
  4. data/README.rdoc +61 -0
  5. data/Rakefile +38 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/stuff_to_do_controller.rb +161 -0
  8. data/app/helpers/stuff_to_do_helper.rb +88 -0
  9. data/app/models/stuff_to_do.rb +208 -0
  10. data/app/models/stuff_to_do_filter.rb +32 -0
  11. data/app/models/stuff_to_do_mailer.rb +16 -0
  12. data/app/views/settings/_stuff_to_do_settings.html.erb +27 -0
  13. data/app/views/stuff_to_do/_issue.html.erb +16 -0
  14. data/app/views/stuff_to_do/_item.html.erb +5 -0
  15. data/app/views/stuff_to_do/_left_panes.html.erb +51 -0
  16. data/app/views/stuff_to_do/_panes.html.erb +11 -0
  17. data/app/views/stuff_to_do/_project.html.erb +6 -0
  18. data/app/views/stuff_to_do/_right_panes.html.erb +25 -0
  19. data/app/views/stuff_to_do/_time_grid.html.erb +113 -0
  20. data/app/views/stuff_to_do/_time_grid_form.html.erb +32 -0
  21. data/app/views/stuff_to_do/index.html.erb +44 -0
  22. data/app/views/stuff_to_do_mailer/recommended_below_threshold.erb +3 -0
  23. data/app/views/stuff_to_do_mailer/recommended_below_threshold.text.html.rhtml +1 -0
  24. data/assets/images/b.png +0 -0
  25. data/assets/images/bl.png +0 -0
  26. data/assets/images/br.png +0 -0
  27. data/assets/images/closelabel.gif +0 -0
  28. data/assets/images/loading.gif +0 -0
  29. data/assets/images/tl.png +0 -0
  30. data/assets/images/tr.png +0 -0
  31. data/assets/javascripts/facebox.js +319 -0
  32. data/assets/javascripts/jquery-1.2.6.min.js +32 -0
  33. data/assets/javascripts/jquery-ui.js +2839 -0
  34. data/assets/javascripts/jquery.contextMenu.js +212 -0
  35. data/assets/javascripts/semantic.cache +15 -0
  36. data/assets/javascripts/stuff-to-do.js +270 -0
  37. data/assets/javascripts/ui/build.xml +24 -0
  38. data/assets/javascripts/ui/effects.blind.js +49 -0
  39. data/assets/javascripts/ui/effects.bounce.js +78 -0
  40. data/assets/javascripts/ui/effects.clip.js +54 -0
  41. data/assets/javascripts/ui/effects.core.js +510 -0
  42. data/assets/javascripts/ui/effects.drop.js +50 -0
  43. data/assets/javascripts/ui/effects.explode.js +79 -0
  44. data/assets/javascripts/ui/effects.fold.js +55 -0
  45. data/assets/javascripts/ui/effects.highlight.js +48 -0
  46. data/assets/javascripts/ui/effects.pulsate.js +55 -0
  47. data/assets/javascripts/ui/effects.scale.js +180 -0
  48. data/assets/javascripts/ui/effects.shake.js +57 -0
  49. data/assets/javascripts/ui/effects.slide.js +50 -0
  50. data/assets/javascripts/ui/effects.transfer.js +59 -0
  51. data/assets/javascripts/ui/i18n/ui.datepicker-ar.js +26 -0
  52. data/assets/javascripts/ui/i18n/ui.datepicker-bg.js +25 -0
  53. data/assets/javascripts/ui/i18n/ui.datepicker-ca.js +25 -0
  54. data/assets/javascripts/ui/i18n/ui.datepicker-cs.js +25 -0
  55. data/assets/javascripts/ui/i18n/ui.datepicker-da.js +25 -0
  56. data/assets/javascripts/ui/i18n/ui.datepicker-de.js +25 -0
  57. data/assets/javascripts/ui/i18n/ui.datepicker-eo.js +25 -0
  58. data/assets/javascripts/ui/i18n/ui.datepicker-es.js +25 -0
  59. data/assets/javascripts/ui/i18n/ui.datepicker-fa.js +25 -0
  60. data/assets/javascripts/ui/i18n/ui.datepicker-fi.js +25 -0
  61. data/assets/javascripts/ui/i18n/ui.datepicker-fr.js +25 -0
  62. data/assets/javascripts/ui/i18n/ui.datepicker-he.js +25 -0
  63. data/assets/javascripts/ui/i18n/ui.datepicker-hr.js +25 -0
  64. data/assets/javascripts/ui/i18n/ui.datepicker-hu.js +25 -0
  65. data/assets/javascripts/ui/i18n/ui.datepicker-hy.js +25 -0
  66. data/assets/javascripts/ui/i18n/ui.datepicker-id.js +25 -0
  67. data/assets/javascripts/ui/i18n/ui.datepicker-is.js +25 -0
  68. data/assets/javascripts/ui/i18n/ui.datepicker-it.js +25 -0
  69. data/assets/javascripts/ui/i18n/ui.datepicker-ja.js +26 -0
  70. data/assets/javascripts/ui/i18n/ui.datepicker-ko.js +25 -0
  71. data/assets/javascripts/ui/i18n/ui.datepicker-lt.js +25 -0
  72. data/assets/javascripts/ui/i18n/ui.datepicker-lv.js +25 -0
  73. data/assets/javascripts/ui/i18n/ui.datepicker-nl.js +25 -0
  74. data/assets/javascripts/ui/i18n/ui.datepicker-no.js +25 -0
  75. data/assets/javascripts/ui/i18n/ui.datepicker-pl.js +25 -0
  76. data/assets/javascripts/ui/i18n/ui.datepicker-pt-BR.js +25 -0
  77. data/assets/javascripts/ui/i18n/ui.datepicker-ro.js +25 -0
  78. data/assets/javascripts/ui/i18n/ui.datepicker-ru.js +25 -0
  79. data/assets/javascripts/ui/i18n/ui.datepicker-sk.js +25 -0
  80. data/assets/javascripts/ui/i18n/ui.datepicker-sl.js +26 -0
  81. data/assets/javascripts/ui/i18n/ui.datepicker-sq.js +25 -0
  82. data/assets/javascripts/ui/i18n/ui.datepicker-sv.js +25 -0
  83. data/assets/javascripts/ui/i18n/ui.datepicker-th.js +25 -0
  84. data/assets/javascripts/ui/i18n/ui.datepicker-tr.js +25 -0
  85. data/assets/javascripts/ui/i18n/ui.datepicker-uk.js +25 -0
  86. data/assets/javascripts/ui/i18n/ui.datepicker-zh-CN.js +25 -0
  87. data/assets/javascripts/ui/i18n/ui.datepicker-zh-TW.js +25 -0
  88. data/assets/javascripts/ui/svn.log +11 -0
  89. data/assets/javascripts/ui/ui.accordion.js +400 -0
  90. data/assets/javascripts/ui/ui.core.js +533 -0
  91. data/assets/javascripts/ui/ui.datepicker.js +1754 -0
  92. data/assets/javascripts/ui/ui.dialog.js +630 -0
  93. data/assets/javascripts/ui/ui.draggable.js +696 -0
  94. data/assets/javascripts/ui/ui.droppable.js +314 -0
  95. data/assets/javascripts/ui/ui.progressbar.js +114 -0
  96. data/assets/javascripts/ui/ui.resizable.js +805 -0
  97. data/assets/javascripts/ui/ui.selectable.js +266 -0
  98. data/assets/javascripts/ui/ui.slider.js +552 -0
  99. data/assets/javascripts/ui/ui.sortable.js +1012 -0
  100. data/assets/javascripts/ui/ui.tabs.js +572 -0
  101. data/assets/stylesheets/stuff_to_do.css +216 -0
  102. data/config/locales/bg.yml +18 -0
  103. data/config/locales/ca-fr.yml +18 -0
  104. data/config/locales/cs.yml +16 -0
  105. data/config/locales/da.yml +16 -0
  106. data/config/locales/de.yml +18 -0
  107. data/config/locales/en.yml +24 -0
  108. data/config/locales/es.yml +19 -0
  109. data/config/locales/fr.yml +17 -0
  110. data/config/locales/hu.yml +16 -0
  111. data/config/locales/it.yml +16 -0
  112. data/config/locales/ja.yml +18 -0
  113. data/config/locales/ko.yml +18 -0
  114. data/config/locales/lt.yml +18 -0
  115. data/config/locales/nl.yml +20 -0
  116. data/config/locales/pt-BR.yml +18 -0
  117. data/config/locales/ru.yml +19 -0
  118. data/config/locales/sv.yml +19 -0
  119. data/config/locales/tr.yml +18 -0
  120. data/config/routes.rb +3 -0
  121. data/init.rb +54 -0
  122. data/lang/bg.yml +17 -0
  123. data/lang/ca-fr.yml +17 -0
  124. data/lang/cs.yml +15 -0
  125. data/lang/da.yml +15 -0
  126. data/lang/de.yml +17 -0
  127. data/lang/en.yml +21 -0
  128. data/lang/es.yml +18 -0
  129. data/lang/fr.yml +16 -0
  130. data/lang/hu.yml +15 -0
  131. data/lang/it.yml +15 -0
  132. data/lang/ja.yml +17 -0
  133. data/lang/ko.yml +17 -0
  134. data/lang/lt.yml +17 -0
  135. data/lang/pt-br.yml +17 -0
  136. data/lang/ru.yml +15 -0
  137. data/lang/sv.yml +18 -0
  138. data/lang/tr.yml +17 -0
  139. data/lib/redmine_stuff_to_do/stuff_to_do_compatibility.rb +15 -0
  140. data/lib/stuff_to_do_array_patch.rb +8 -0
  141. data/lib/stuff_to_do_issue_patch.rb +57 -0
  142. data/lib/stuff_to_do_project_patch.rb +31 -0
  143. data/lib/stuff_to_do_user_patch.rb +10 -0
  144. data/rails/init.rb +1 -0
  145. data/spec/controllers/stuff_to_do_controller_add_to_time_grid_spec.rb +58 -0
  146. data/spec/controllers/stuff_to_do_controller_index_spec.rb +155 -0
  147. data/spec/controllers/stuff_to_do_controller_remove_from_time_grid_spec.rb +56 -0
  148. data/spec/controllers/stuff_to_do_controller_reorder_spec.rb +179 -0
  149. data/spec/controllers/stuff_to_do_controller_save_time_entries_spec.rb +56 -0
  150. data/spec/controllers/stuff_to_do_private_methods_spec.rb +82 -0
  151. data/spec/lib/stuff_to_do_issue_patch_spec.rb +60 -0
  152. data/spec/lib/stuff_to_do_project_patch_spec.rb +50 -0
  153. data/spec/lib/stuff_to_do_user_patch_spec.rb +8 -0
  154. data/spec/models/stuff_to_do_filter_spec.rb +3 -0
  155. data/spec/models/stuff_to_do_mailer_spec.rb +42 -0
  156. data/spec/models/stuff_to_do_spec.rb +426 -0
  157. data/spec/sanity_spec.rb +7 -0
  158. data/spec/spec_helper.rb +130 -0
  159. metadata +211 -0
@@ -0,0 +1,212 @@
1
+ // jQuery Context Menu Plugin
2
+ //
3
+ // Version 1.00
4
+ //
5
+ // Cory S.N. LaViska
6
+ // A Beautiful Site (http://abeautifulsite.net/)
7
+ //
8
+ // Visit http://abeautifulsite.net/notebook/80 for usage and more information
9
+ //
10
+ // Terms of Use
11
+ //
12
+ // This software is licensed under a Creative Commons License and is copyrighted
13
+ // (C)2008 by Cory S.N. LaViska.
14
+ //
15
+ // For details, visit http://creativecommons.org/licenses/by/3.0/us/
16
+ //
17
+ if(jQuery)( function($) {
18
+ $.extend($.fn, {
19
+
20
+ contextMenu: function(o, callback) {
21
+ // Defaults
22
+ if( o.menu == undefined ) return false;
23
+ if( o.menuCssName == undefined ) o.menuCssName = 'contextMenu';
24
+ if( o.inSpeed == undefined ) o.inSpeed = 150;
25
+ if( o.outSpeed == undefined ) o.outSpeed = 75;
26
+ // 0 needs to be -1 for expected results (no fade)
27
+ if( o.inSpeed == 0 ) o.inSpeed = -1;
28
+ if( o.outSpeed == 0 ) o.outSpeed = -1;
29
+ // Loop each context menu
30
+ $(this).each( function() {
31
+ var el = $(this);
32
+ var offset = $(el).offset();
33
+ // Add contextMenu class
34
+ $('#' + o.menu).addClass(o.menuCssName);
35
+ // Simulate a true right click
36
+ $(this).mousedown( function(e) {
37
+ var evt = e;
38
+ $(this).mouseup( function(e) {
39
+ var srcElement = $(this);
40
+ $(this).unbind('mouseup');
41
+ if( evt.button == 2 ) {
42
+ // Hide context menus that may be showing
43
+ $("." + o.menuCssName).hide();
44
+ // Get this context menu
45
+ var menu = $('#' + o.menu);
46
+
47
+ if( $(el).hasClass('disabled') ) return false;
48
+
49
+ // Detect mouse position
50
+ var d = {}, x, y;
51
+ if( self.innerHeight ) {
52
+ d.pageYOffset = self.pageYOffset;
53
+ d.pageXOffset = self.pageXOffset;
54
+ d.innerHeight = self.innerHeight;
55
+ d.innerWidth = self.innerWidth;
56
+ } else if( document.documentElement &&
57
+ document.documentElement.clientHeight ) {
58
+ d.pageYOffset = document.documentElement.scrollTop;
59
+ d.pageXOffset = document.documentElement.scrollLeft;
60
+ d.innerHeight = document.documentElement.clientHeight;
61
+ d.innerWidth = document.documentElement.clientWidth;
62
+ } else if( document.body ) {
63
+ d.pageYOffset = document.body.scrollTop;
64
+ d.pageXOffset = document.body.scrollLeft;
65
+ d.innerHeight = document.body.clientHeight;
66
+ d.innerWidth = document.body.clientWidth;
67
+ }
68
+ (e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
69
+ (e.pageY) ? y = e.pageY : x = e.clientY + d.scrollTop;
70
+
71
+ // Show the menu
72
+ $(document).unbind('click');
73
+ $(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);
74
+ // Hover events
75
+ $(menu).find('A').mouseover( function() {
76
+ $(menu).find('LI.hover').removeClass('hover');
77
+ $(this).parent().addClass('hover');
78
+ }).mouseout( function() {
79
+ $(menu).find('LI.hover').removeClass('hover');
80
+ });
81
+
82
+ // Keyboard
83
+ $(document).keypress( function(e) {
84
+ switch( e.keyCode ) {
85
+ case 38: // up
86
+ if( $(menu).find('LI.hover').size() == 0 ) {
87
+ $(menu).find('LI:last').addClass('hover');
88
+ } else {
89
+ $(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');
90
+ if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:last').addClass('hover');
91
+ }
92
+ break;
93
+ case 40: // down
94
+ if( $(menu).find('LI.hover').size() == 0 ) {
95
+ $(menu).find('LI:first').addClass('hover');
96
+ } else {
97
+ $(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');
98
+ if( $(menu).find('LI.hover').size() == 0 ) $(menu).find('LI:first').addClass('hover');
99
+ }
100
+ break;
101
+ case 13: // enter
102
+ $(menu).find('LI.hover A').trigger('click');
103
+ break;
104
+ case 27: // esc
105
+ $(document).trigger('click');
106
+ break
107
+ }
108
+ });
109
+
110
+ // When items are selected
111
+ $('#' + o.menu).find('A').unbind('click');
112
+ $('#' + o.menu).find('LI:not(.disabled) A').click( function() {
113
+ $(document).unbind('click').unbind('keypress');
114
+ $("." + o.menuCssName).hide();
115
+ // Callback
116
+ if( callback ) callback( $(this).attr('href').substr(1), $(srcElement), {x: x - offset.left, y: y - offset.top, docX: x, docY: y} );
117
+ return false;
118
+ });
119
+
120
+ // Hide bindings
121
+ setTimeout( function() { // Delay for Mozilla
122
+ $(document).click( function() {
123
+ $(document).unbind('click').unbind('keypress');
124
+ $(menu).fadeOut(o.outSpeed);
125
+ return false;
126
+ });
127
+ }, 0);
128
+ }
129
+ });
130
+ });
131
+
132
+ // Disable text selection
133
+ if( $.browser.mozilla ) {
134
+ $('#' + o.menu).each( function() { $(this).css({ 'MozUserSelect' : 'none' }); });
135
+ } else if( $.browser.msie ) {
136
+ $('#' + o.menu).each( function() { $(this).bind('selectstart.disableTextSelect', function() { return false; }); });
137
+ } else {
138
+ $('#' + o.menu).each(function() { $(this).bind('mousedown.disableTextSelect', function() { return false; }); });
139
+ }
140
+ // Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
141
+ $(el).add('UL.' + o.menuCssName).bind('contextmenu', function() { return false; });
142
+
143
+ });
144
+ return $(this);
145
+ },
146
+
147
+ // Disable context menu items on the fly
148
+ disableContextMenuItems: function(o) {
149
+ if( o == undefined ) {
150
+ // Disable all
151
+ $(this).find('LI').addClass('disabled');
152
+ return( $(this) );
153
+ }
154
+ $(this).each( function() {
155
+ if( o != undefined ) {
156
+ var d = o.split(',');
157
+ for( var i = 0; i < d.length; i++ ) {
158
+ $(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');
159
+
160
+ }
161
+ }
162
+ });
163
+ return( $(this) );
164
+ },
165
+
166
+ // Enable context menu items on the fly
167
+ enableContextMenuItems: function(o) {
168
+ if( o == undefined ) {
169
+ // Enable all
170
+ $(this).find('LI.disabled').removeClass('disabled');
171
+ return( $(this) );
172
+ }
173
+ $(this).each( function() {
174
+ if( o != undefined ) {
175
+ var d = o.split(',');
176
+ for( var i = 0; i < d.length; i++ ) {
177
+ $(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');
178
+
179
+ }
180
+ }
181
+ });
182
+ return( $(this) );
183
+ },
184
+
185
+ // Disable context menu(s)
186
+ disableContextMenu: function() {
187
+ $(this).each( function() {
188
+ $(this).addClass('disabled');
189
+ });
190
+ return( $(this) );
191
+ },
192
+
193
+ // Enable context menu(s)
194
+ enableContextMenu: function() {
195
+ $(this).each( function() {
196
+ $(this).removeClass('disabled');
197
+ });
198
+ return( $(this) );
199
+ },
200
+
201
+ // Destroy context menu(s)
202
+ destroyContextMenu: function() {
203
+ // Destroy specified context menus
204
+ $(this).each( function() {
205
+ // Disable action
206
+ $(this).unbind('mousedown').unbind('mouseup');
207
+ });
208
+ return( $(this) );
209
+ }
210
+
211
+ });
212
+ })(jQuery);
@@ -0,0 +1,15 @@
1
+ ;; Object javascripts/
2
+ ;; SEMANTICDB Tags save file
3
+ (semanticdb-project-database-file "javascripts/"
4
+ :tables (list
5
+ (semanticdb-table "stuff-to-do.js"
6
+ :major-mode 'javascript-mode
7
+ :tags '(("attachSortables" function nil nil [938 1844]) ("saveOrder" function nil nil [1846 2464]))
8
+ :file "stuff-to-do.js"
9
+ :pointmax 2466
10
+ )
11
+ )
12
+ :file "semantic.cache"
13
+ :semantic-tag-version "2.0pre4"
14
+ :semanticdb-version "2.0pre4"
15
+ )
@@ -0,0 +1,270 @@
1
+ // TODO: JSUnit test this
2
+ jQuery(function($) {
3
+ $("#user_id").change(function() { $("form#user_switch").submit(); });
4
+ $("#ajax-indicator").ajaxStart(function(){ $(this).show(); });
5
+ $("#ajax-indicator").ajaxStop(function(){ $(this).hide(); });
6
+
7
+ $("#filter").change(function() {
8
+ if ($('#filter').val() != '') {
9
+ $.ajax({
10
+ type: "GET",
11
+ url: 'stuff_to_do/available_issues.js',
12
+ data: $('#filter').serialize(),
13
+ success: function(response) {
14
+ $('#available-pane').html(response);
15
+ attachSortables();
16
+ },
17
+ error: function(response) {
18
+ $("div.error").html("Error filtering pane. Please refresh the page.").show();
19
+ }});
20
+ }
21
+ });
22
+
23
+ attachSortables = function() {
24
+ $("#available").sortable({
25
+ cancel: 'a',
26
+ connectWith: ["#doing-now", "#recommended", "#time-grid-table tbody"],
27
+ placeholder: 'drop-accepted',
28
+ dropOnEmpty: true,
29
+ update : function (event, ui) {
30
+ if ($('#available li.issue').length > 0) {
31
+ $("#available li.empty-list").hide();
32
+ } else {
33
+ $("#available li.empty-list").show();
34
+ }
35
+ },
36
+ receive : function (event, ui) { removeItemFromTimeGridIfNeeded(event,ui);}
37
+ });
38
+
39
+ $("#doing-now").sortable({
40
+ cancel: 'a',
41
+ connectWith: ["#available", "#recommended", "#time-grid-table tbody"],
42
+ dropOnEmpty: true,
43
+ placeholder: 'drop-accepted',
44
+ update : function (event, ui) { saveOrder(ui); },
45
+ receive : function (event, ui) { removeItemFromTimeGridIfNeeded(event,ui);}
46
+ });
47
+
48
+ $("#recommended").sortable({
49
+ cancel: 'a',
50
+ connectWith: ["#available", "#doing-now", "#time-grid-table tbody"],
51
+ dropOnEmpty: true,
52
+ placeholder: 'drop-accepted',
53
+ update : function (event, ui) { saveOrder(ui); },
54
+ receive : function (event, ui) { removeItemFromTimeGridIfNeeded(event,ui);}
55
+ });
56
+
57
+ $("#time-grid-table tbody").sortable({
58
+ connectWith: ["#available", "#doing-now", "#recommended"],
59
+ items: 'tr',
60
+ placeholder: 'drop-accepted',
61
+ // Cancel the drag and drop if it's reordering itself
62
+ update: function (event, ui) {
63
+ if (ui.sender == null && isDraggingToTimeGrid($(event.target))) {
64
+ $(this).sortable('cancel');
65
+ }
66
+ },
67
+ receive : function (event, ui) {
68
+ $(ui.sender).sortable('cancel');
69
+ if (isAddingAnIssueToTimeGrid($(event.target))) {
70
+ var std_item = ui.item;
71
+ // Only add issues that are missing.
72
+ if (!isProjectItem(std_item) && !isItemInTimeGrid(std_item)) {
73
+ addItemToTimeGrid(std_item);
74
+ }
75
+ }
76
+ }
77
+ });
78
+ },
79
+
80
+ saveOrder = function() {
81
+ data = 'user_id=' + user_id + '&' + $("#doing-now").sortable('serialize') + '&' + $("#recommended").sortable('serialize');
82
+ if (filter != null) {
83
+ data = data + '&filter=' + filter;
84
+ }
85
+
86
+ data = addAuthenticityToken(data);
87
+
88
+ $.ajax({
89
+ type: "POST",
90
+ url: 'stuff_to_do/reorder.js',
91
+ data: data,
92
+ success: function(response) {
93
+ $('#panes').html(response);
94
+ attachSortables();
95
+ },
96
+ error: function(response) {
97
+ $("div#stuff-to-do-error").html("Error saving lists. Please refresh the page and try again.").show();
98
+ }});
99
+
100
+ },
101
+
102
+ addItemToTimeGrid = function(issue) {
103
+ $.ajax({
104
+ type: "POST",
105
+ url: 'stuff_to_do/add_to_time_grid.js',
106
+ data: addAuthenticityToken('issue_id=' + getRecordId(issue) + '&' + $('#query_form').serialize()),
107
+ success: function(response) {
108
+ $('#time-grid').html(response);
109
+ attachSortables();
110
+ },
111
+ error: function(response) {
112
+ $("div#time-grid-error").html("Error saving Time Grid. Please refresh the page and try again.").show();
113
+ }});
114
+ },
115
+
116
+ removeItemFromTimeGrid = function(issue) {
117
+ $.ajax({
118
+ type: "POST",
119
+ url: 'stuff_to_do/remove_from_time_grid.js',
120
+ data: addAuthenticityToken('issue_id=' + getRecordId(issue) + '&' + $('#query_form').serialize()),
121
+ success: function(response) {
122
+ $('#time-grid').html(response);
123
+ attachSortables();
124
+ },
125
+ error: function(response) {
126
+ $("div#time-grid-error").html("Error saving Time Grid. Please refresh the page and try again.").show();
127
+ }});
128
+ },
129
+
130
+ isProjectItem = function(element) {
131
+ return element.attr('id').match(/project/);
132
+ },
133
+
134
+ isItemInTimeGrid = function(element) {
135
+ var record_id = getRecordId(element);
136
+ return $('td.time-grid-issue issue_' + record_id).size() > 0;
137
+ },
138
+
139
+ isAddingAnIssueToTimeGrid = function(jqueryElement) {
140
+ return (jqueryElement.parents('#time-grid-table').length == 0);
141
+ },
142
+
143
+ isDraggingToTimeGrid = function(jqueryElement) {
144
+ return !isAddingAnIssueToTimeGrid(jqueryElement);
145
+ },
146
+
147
+ getRecordId = function(jqueryElement) {
148
+ return jqueryElement.attr('id').split('_').last();
149
+ },
150
+
151
+ removeItemFromTimeGridIfNeeded = function (event, ui) {
152
+ if (!isAddingAnIssueToTimeGrid($(event.target)) || !isAddingAnIssueToTimeGrid(ui.sender)) {
153
+ $(ui.sender).sortable('cancel');
154
+ removeItemFromTimeGrid(ui.item);
155
+ }
156
+ },
157
+
158
+ timeLogFacebox = function(issue_id, date) {
159
+ if (issue_id != undefined) {
160
+ $('#time_entry__issue_id').val(issue_id);
161
+ }
162
+
163
+ if (date != undefined) {
164
+ $('#time_entry__core').val(date); // Renamed below
165
+ }
166
+
167
+ $.facebox({div: '#logtime'});
168
+ bindTimeEntryForm(); // Rebind since Facebox copies it
169
+ // Need to rebind the calendar by renaming the time_entry
170
+ // fields and running setup.
171
+ $('#facebox #time_entry__core').attr("id", "time_entry__spent_on");
172
+ $('#facebox #time_entry__core_trigger').attr("id", "time_entry__spent_on_trigger");
173
+ Calendar.setup({inputField : 'time_entry__spent_on', ifFormat : '%Y-%m-%d', button : 'time_entry__spent_on_trigger' });
174
+ },
175
+
176
+ parseIssueId = function(jqueryElement) {
177
+ return jqueryElement.attr('id').split('_')[1];
178
+ },
179
+
180
+ parseDateFromGrid = function(jqueryElement) {
181
+ return jqueryElement.attr('class').split(' ')[1];
182
+ },
183
+
184
+ saveTimeEntriesRemotely = function(form) {
185
+ $.ajax({
186
+ type: "POST",
187
+ url: 'stuff_to_do/save_time_entry.js',
188
+ data: addAuthenticityToken($(form).serialize()),
189
+ success: function(response) {
190
+ $('#time-grid').before(response).remove();
191
+ $('.time-grid-flash').not(':empty').show();
192
+ jQuery(document).trigger('close.facebox');
193
+ },
194
+ error: function(response) {
195
+ alert(response.responseText);
196
+ }
197
+ });
198
+ },
199
+
200
+ bindContextMenuToTimeGrid = function() {
201
+ $("#time-grid-table tr td.time-grid-date").
202
+ contextMenu({ menu: 'time-grid-menu', menuCssName: 'context-menu' },
203
+ function(action, el, pos) {
204
+ timeLogFacebox(parseIssueId(el.parent()),
205
+ parseDateFromGrid(el));
206
+ });
207
+ },
208
+
209
+ bindTimeEntryForm = function() {
210
+ $('#facebox #logtime form').submit(function(){
211
+ saveTimeEntriesRemotely(this);
212
+ return false;
213
+ });
214
+ },
215
+
216
+ bindSaveTimeGridButtons = function() {
217
+ $('.save-time-grid').click(function(){
218
+ saveTimeEntriesRemotely();
219
+ return false;
220
+ });
221
+ },
222
+
223
+ initTimeGrid = function() {
224
+ bindContextMenuToTimeGrid();
225
+ bindTimeEntryForm();
226
+ bindSaveTimeGridButtons();
227
+ },
228
+
229
+ addAuthenticityToken = function(data) {
230
+ return data + '&authenticity_token=' + encodeURIComponent(window._token);
231
+ },
232
+
233
+ attachSortables();
234
+
235
+ // Fix the image paths in facebox
236
+ $.extend($.facebox.settings, {
237
+ loadingImage: '../images/loading.gif',
238
+ closeImage: '../plugin_assets/stuff_to_do_plugin/images/closelabel.gif',
239
+ faceboxHtml : '\
240
+ <div id="facebox" style="display:none;"> \
241
+ <div class="popup"> \
242
+ <table> \
243
+ <tbody> \
244
+ <tr> \
245
+ <td class="tl"/><td class="b"/><td class="tr"/> \
246
+ </tr> \
247
+ <tr> \
248
+ <td class="b"/> \
249
+ <td class="body"> \
250
+ <div class="content"> \
251
+ </div> \
252
+ <div class="footer"> \
253
+ <a href="#" class="close"> \
254
+ <img src="../plugin_assets/stuff_to_do_plugin/images/closelabel.gif" title="close" class="close_image" /> \
255
+ </a> \
256
+ </div> \
257
+ </td> \
258
+ <td class="b"/> \
259
+ </tr> \
260
+ <tr> \
261
+ <td class="bl"/><td class="b"/><td class="br"/> \
262
+ </tr> \
263
+ </tbody> \
264
+ </table> \
265
+ </div> \
266
+ </div>'
267
+
268
+ });
269
+
270
+ });