merb-admin 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +58 -0
  3. data/Rakefile +64 -0
  4. data/app/controllers/application.rb +6 -0
  5. data/app/controllers/main.rb +145 -0
  6. data/app/helpers/application_helper.rb +64 -0
  7. data/app/helpers/main_helper.rb +122 -0
  8. data/app/views/layout/_message.html.erb +10 -0
  9. data/app/views/layout/dashboard.html.erb +34 -0
  10. data/app/views/layout/form.html.erb +48 -0
  11. data/app/views/layout/list.html.erb +42 -0
  12. data/app/views/main/_belongs_to.html.erb +29 -0
  13. data/app/views/main/_big_decimal.html.erb +12 -0
  14. data/app/views/main/_boolean.html.erb +7 -0
  15. data/app/views/main/_date.html.erb +12 -0
  16. data/app/views/main/_datetime.html.erb +12 -0
  17. data/app/views/main/_float.html.erb +12 -0
  18. data/app/views/main/_has_many.html.erb +16 -0
  19. data/app/views/main/_has_one.html.erb +30 -0
  20. data/app/views/main/_integer.html.erb +18 -0
  21. data/app/views/main/_properties.html.erb +18 -0
  22. data/app/views/main/_string.html.erb +12 -0
  23. data/app/views/main/_text.html.erb +11 -0
  24. data/app/views/main/_time.html.erb +12 -0
  25. data/app/views/main/_timestamp.html.erb +12 -0
  26. data/app/views/main/delete.html.erb +12 -0
  27. data/app/views/main/edit.html.erb +25 -0
  28. data/app/views/main/index.html.erb +22 -0
  29. data/app/views/main/list.html.erb +72 -0
  30. data/app/views/main/new.html.erb +22 -0
  31. data/lib/abstract_model.rb +67 -0
  32. data/lib/activerecord_support.rb +169 -0
  33. data/lib/datamapper_support.rb +157 -0
  34. data/lib/generic_support.rb +17 -0
  35. data/lib/merb-admin/merbtasks.rb +103 -0
  36. data/lib/merb-admin/slicetasks.rb +20 -0
  37. data/lib/merb-admin/spectasks.rb +53 -0
  38. data/lib/merb-admin.rb +99 -0
  39. data/public/images/arrow-down.gif +0 -0
  40. data/public/images/arrow-up.gif +0 -0
  41. data/public/images/changelist-bg.gif +0 -0
  42. data/public/images/changelist-bg_rtl.gif +0 -0
  43. data/public/images/chooser-bg.gif +0 -0
  44. data/public/images/chooser_stacked-bg.gif +0 -0
  45. data/public/images/default-bg-reverse.gif +0 -0
  46. data/public/images/default-bg.gif +0 -0
  47. data/public/images/deleted-overlay.gif +0 -0
  48. data/public/images/icon-no.gif +0 -0
  49. data/public/images/icon-unknown.gif +0 -0
  50. data/public/images/icon-yes.gif +0 -0
  51. data/public/images/icon_addlink.gif +0 -0
  52. data/public/images/icon_alert.gif +0 -0
  53. data/public/images/icon_calendar.gif +0 -0
  54. data/public/images/icon_changelink.gif +0 -0
  55. data/public/images/icon_clock.gif +0 -0
  56. data/public/images/icon_deletelink.gif +0 -0
  57. data/public/images/icon_error.gif +0 -0
  58. data/public/images/icon_searchbox.png +0 -0
  59. data/public/images/icon_success.gif +0 -0
  60. data/public/images/inline-delete-8bit.png +0 -0
  61. data/public/images/inline-delete.png +0 -0
  62. data/public/images/inline-restore-8bit.png +0 -0
  63. data/public/images/inline-restore.png +0 -0
  64. data/public/images/inline-splitter-bg.gif +0 -0
  65. data/public/images/nav-bg-grabber.gif +0 -0
  66. data/public/images/nav-bg-reverse.gif +0 -0
  67. data/public/images/nav-bg.gif +0 -0
  68. data/public/images/selector-add.gif +0 -0
  69. data/public/images/selector-addall.gif +0 -0
  70. data/public/images/selector-remove.gif +0 -0
  71. data/public/images/selector-removeall.gif +0 -0
  72. data/public/images/selector-search.gif +0 -0
  73. data/public/images/selector_stacked-add.gif +0 -0
  74. data/public/images/selector_stacked-remove.gif +0 -0
  75. data/public/images/tool-left.gif +0 -0
  76. data/public/images/tool-left_over.gif +0 -0
  77. data/public/images/tool-right.gif +0 -0
  78. data/public/images/tool-right_over.gif +0 -0
  79. data/public/images/tooltag-add.gif +0 -0
  80. data/public/images/tooltag-add_over.gif +0 -0
  81. data/public/images/tooltag-arrowright.gif +0 -0
  82. data/public/images/tooltag-arrowright_over.gif +0 -0
  83. data/public/javascripts/CollapsedFieldsets.js +85 -0
  84. data/public/javascripts/DateTimeShortcuts.js +255 -0
  85. data/public/javascripts/RelatedObjectLookups.js +96 -0
  86. data/public/javascripts/SelectBox.js +111 -0
  87. data/public/javascripts/SelectFilter2.js +113 -0
  88. data/public/javascripts/actions.js +39 -0
  89. data/public/javascripts/calendar.js +143 -0
  90. data/public/javascripts/core.js +176 -0
  91. data/public/javascripts/dateparse.js +233 -0
  92. data/public/javascripts/getElementsBySelector.js +167 -0
  93. data/public/javascripts/i18n.js +33 -0
  94. data/public/javascripts/master.js +0 -0
  95. data/public/javascripts/ordering.js +137 -0
  96. data/public/javascripts/timeparse.js +94 -0
  97. data/public/javascripts/urlify.js +140 -0
  98. data/public/stylesheets/base.css +746 -0
  99. data/public/stylesheets/changelists.css +269 -0
  100. data/public/stylesheets/dashboard.css +24 -0
  101. data/public/stylesheets/forms.css +327 -0
  102. data/public/stylesheets/global.css +142 -0
  103. data/public/stylesheets/ie.css +51 -0
  104. data/public/stylesheets/layout.css +29 -0
  105. data/public/stylesheets/login.css +54 -0
  106. data/public/stylesheets/master.css +2 -0
  107. data/public/stylesheets/null.css +1 -0
  108. data/public/stylesheets/patch-iewin.css +8 -0
  109. data/public/stylesheets/rtl.css +206 -0
  110. data/public/stylesheets/widgets.css +506 -0
  111. data/schema/migrations/001_create_divisions_migration.rb +15 -0
  112. data/schema/migrations/002_create_drafts_migration.rb +21 -0
  113. data/schema/migrations/003_create_leagues_migration.rb +13 -0
  114. data/schema/migrations/004_create_players_migration.rb +23 -0
  115. data/schema/migrations/005_create_teams_migration.rb +18 -0
  116. data/spec/controllers/main_spec.rb +25 -0
  117. data/spec/models/activerecord/division.rb +8 -0
  118. data/spec/models/activerecord/draft.rb +16 -0
  119. data/spec/models/activerecord/league.rb +6 -0
  120. data/spec/models/activerecord/player.rb +10 -0
  121. data/spec/models/activerecord/team.rb +11 -0
  122. data/spec/models/datamapper/division.rb +12 -0
  123. data/spec/models/datamapper/draft.rb +18 -0
  124. data/spec/models/datamapper/league.rb +11 -0
  125. data/spec/models/datamapper/player.rb +21 -0
  126. data/spec/models/datamapper/team.rb +15 -0
  127. data/spec/requests/main_spec.rb +475 -0
  128. data/spec/spec_helper.rb +112 -0
  129. metadata +209 -0
@@ -0,0 +1,255 @@
1
+ // Inserts shortcut buttons after all of the following:
2
+ // <input type="text" class="vDateField">
3
+ // <input type="text" class="vTimeField">
4
+
5
+ var DateTimeShortcuts = {
6
+ calendars: [],
7
+ calendarInputs: [],
8
+ clockInputs: [],
9
+ calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
10
+ calendarDivName2: 'calendarin', // name of <div> that contains calendar
11
+ calendarLinkName: 'calendarlink',// name of the link that is used to toggle
12
+ clockDivName: 'clockbox', // name of clock <div> that gets toggled
13
+ clockLinkName: 'clocklink', // name of the link that is used to toggle
14
+ admin_media_prefix: '',
15
+ init: function() {
16
+ // Deduce admin_media_prefix by looking at the <script>s in the
17
+ // current document and finding the URL of *this* module.
18
+ var scripts = document.getElementsByTagName('script');
19
+ for (var i=0; i<scripts.length; i++) {
20
+ if (scripts[i].src.match(/DateTimeShortcuts/)) {
21
+ var idx = scripts[i].src.indexOf('DateTimeShortcuts');
22
+ DateTimeShortcuts.admin_media_prefix = scripts[i].src.substring(0, idx);
23
+ break;
24
+ }
25
+ }
26
+
27
+ var inputs = document.getElementsByTagName('input');
28
+ for (i=0; i<inputs.length; i++) {
29
+ var inp = inputs[i];
30
+ if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
31
+ DateTimeShortcuts.addClock(inp);
32
+ }
33
+ else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
34
+ DateTimeShortcuts.addCalendar(inp);
35
+ }
36
+ }
37
+ },
38
+ // Add clock widget to a given field
39
+ addClock: function(inp) {
40
+ var num = DateTimeShortcuts.clockInputs.length;
41
+ DateTimeShortcuts.clockInputs[num] = inp;
42
+
43
+ // Shortcut links (clock icon and "Now" link)
44
+ var shortcuts_span = document.createElement('span');
45
+ inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
46
+ var now_link = document.createElement('a');
47
+ now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());");
48
+ now_link.appendChild(document.createTextNode(gettext('Now')));
49
+ var clock_link = document.createElement('a');
50
+ clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
51
+ clock_link.id = DateTimeShortcuts.clockLinkName + num;
52
+ quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + '../images/icon_clock.gif', 'alt', gettext('Clock'));
53
+ shortcuts_span.appendChild(document.createTextNode('\240'));
54
+ shortcuts_span.appendChild(now_link);
55
+ shortcuts_span.appendChild(document.createTextNode('\240|\240'));
56
+ shortcuts_span.appendChild(clock_link);
57
+
58
+ // Create clock link div
59
+ //
60
+ // Markup looks like:
61
+ // <div id="clockbox1" class="clockbox module">
62
+ // <h2>Choose a time</h2>
63
+ // <ul class="timelist">
64
+ // <li><a href="#">Now</a></li>
65
+ // <li><a href="#">Midnight</a></li>
66
+ // <li><a href="#">6 a.m.</a></li>
67
+ // <li><a href="#">Noon</a></li>
68
+ // </ul>
69
+ // <p class="calendar-cancel"><a href="#">Cancel</a></p>
70
+ // </div>
71
+
72
+ var clock_box = document.createElement('div');
73
+ clock_box.style.display = 'none';
74
+ clock_box.style.position = 'absolute';
75
+ clock_box.className = 'clockbox module';
76
+ clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
77
+ document.body.appendChild(clock_box);
78
+ addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);
79
+
80
+ quickElement('h2', clock_box, gettext('Choose a time'));
81
+ time_list = quickElement('ul', clock_box, '');
82
+ time_list.className = 'timelist';
83
+ quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());")
84
+ quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00:00');")
85
+ quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00:00');")
86
+ quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00:00');")
87
+
88
+ cancel_p = quickElement('p', clock_box, '');
89
+ cancel_p.className = 'calendar-cancel';
90
+ quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
91
+ },
92
+ openClock: function(num) {
93
+ var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
94
+ var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
95
+
96
+ // Recalculate the clockbox position
97
+ // is it left-to-right or right-to-left layout ?
98
+ if (getStyle(document.body,'direction')!='rtl') {
99
+ clock_box.style.left = findPosX(clock_link) + 17 + 'px';
100
+ }
101
+ else {
102
+ // since style's width is in em, it'd be tough to calculate
103
+ // px value of it. let's use an estimated px for now
104
+ // TODO: IE returns wrong value for findPosX when in rtl mode
105
+ // (it returns as it was left aligned), needs to be fixed.
106
+ clock_box.style.left = findPosX(clock_link) - 110 + 'px';
107
+ }
108
+ clock_box.style.top = findPosY(clock_link) - 30 + 'px';
109
+
110
+ // Show the clock box
111
+ clock_box.style.display = 'block';
112
+ addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
113
+ },
114
+ dismissClock: function(num) {
115
+ document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
116
+ window.document.onclick = null;
117
+ },
118
+ handleClockQuicklink: function(num, val) {
119
+ DateTimeShortcuts.clockInputs[num].value = val;
120
+ DateTimeShortcuts.dismissClock(num);
121
+ },
122
+ // Add calendar widget to a given field.
123
+ addCalendar: function(inp) {
124
+ var num = DateTimeShortcuts.calendars.length;
125
+
126
+ DateTimeShortcuts.calendarInputs[num] = inp;
127
+
128
+ // Shortcut links (calendar icon and "Today" link)
129
+ var shortcuts_span = document.createElement('span');
130
+ inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
131
+ var today_link = document.createElement('a');
132
+ today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
133
+ today_link.appendChild(document.createTextNode(gettext('Today')));
134
+ var cal_link = document.createElement('a');
135
+ cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
136
+ cal_link.id = DateTimeShortcuts.calendarLinkName + num;
137
+ quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + '../images/icon_calendar.gif', 'alt', gettext('Calendar'));
138
+ shortcuts_span.appendChild(document.createTextNode('\240'));
139
+ shortcuts_span.appendChild(today_link);
140
+ shortcuts_span.appendChild(document.createTextNode('\240|\240'));
141
+ shortcuts_span.appendChild(cal_link);
142
+
143
+ // Create calendarbox div.
144
+ //
145
+ // Markup looks like:
146
+ //
147
+ // <div id="calendarbox3" class="calendarbox module">
148
+ // <h2>
149
+ // <a href="#" class="link-previous">&lsaquo;</a>
150
+ // <a href="#" class="link-next">&rsaquo;</a> February 2003
151
+ // </h2>
152
+ // <div class="calendar" id="calendarin3">
153
+ // <!-- (cal) -->
154
+ // </div>
155
+ // <div class="calendar-shortcuts">
156
+ // <a href="#">Yesterday</a> | <a href="#">Today</a> | <a href="#">Tomorrow</a>
157
+ // </div>
158
+ // <p class="calendar-cancel"><a href="#">Cancel</a></p>
159
+ // </div>
160
+ var cal_box = document.createElement('div');
161
+ cal_box.style.display = 'none';
162
+ cal_box.style.position = 'absolute';
163
+ cal_box.className = 'calendarbox module';
164
+ cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
165
+ document.body.appendChild(cal_box);
166
+ addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);
167
+
168
+ // next-prev links
169
+ var cal_nav = quickElement('div', cal_box, '');
170
+ var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');
171
+ cal_nav_prev.className = 'calendarnav-previous';
172
+ var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');
173
+ cal_nav_next.className = 'calendarnav-next';
174
+
175
+ // main box
176
+ var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);
177
+ cal_main.className = 'calendar';
178
+ DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));
179
+ DateTimeShortcuts.calendars[num].drawCurrent();
180
+
181
+ // calendar shortcuts
182
+ var shortcuts = quickElement('div', cal_box, '');
183
+ shortcuts.className = 'calendar-shortcuts';
184
+ quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');
185
+ shortcuts.appendChild(document.createTextNode('\240|\240'));
186
+ quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');
187
+ shortcuts.appendChild(document.createTextNode('\240|\240'));
188
+ quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');
189
+
190
+ // cancel bar
191
+ var cancel_p = quickElement('p', cal_box, '');
192
+ cancel_p.className = 'calendar-cancel';
193
+ quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
194
+ },
195
+ openCalendar: function(num) {
196
+ var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
197
+ var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
198
+ var inp = DateTimeShortcuts.calendarInputs[num];
199
+
200
+ // Determine if the current value in the input has a valid date.
201
+ // If so, draw the calendar with that date's year and month.
202
+ if (inp.value) {
203
+ var date_parts = inp.value.split('-');
204
+ var year = date_parts[0];
205
+ var month = parseFloat(date_parts[1]);
206
+ if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
207
+ DateTimeShortcuts.calendars[num].drawDate(month, year);
208
+ }
209
+ }
210
+
211
+
212
+ // Recalculate the clockbox position
213
+ // is it left-to-right or right-to-left layout ?
214
+ if (getStyle(document.body,'direction')!='rtl') {
215
+ cal_box.style.left = findPosX(cal_link) + 17 + 'px';
216
+ }
217
+ else {
218
+ // since style's width is in em, it'd be tough to calculate
219
+ // px value of it. let's use an estimated px for now
220
+ // TODO: IE returns wrong value for findPosX when in rtl mode
221
+ // (it returns as it was left aligned), needs to be fixed.
222
+ cal_box.style.left = findPosX(cal_link) - 180 + 'px';
223
+ }
224
+ cal_box.style.top = findPosY(cal_link) - 75 + 'px';
225
+
226
+ cal_box.style.display = 'block';
227
+ addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
228
+ },
229
+ dismissCalendar: function(num) {
230
+ document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';
231
+ window.document.onclick = null;
232
+ },
233
+ drawPrev: function(num) {
234
+ DateTimeShortcuts.calendars[num].drawPreviousMonth();
235
+ },
236
+ drawNext: function(num) {
237
+ DateTimeShortcuts.calendars[num].drawNextMonth();
238
+ },
239
+ handleCalendarCallback: function(num) {
240
+ return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = y+'-'+(m<10?'0':'')+m+'-'+(d<10?'0':'')+d; document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
241
+ },
242
+ handleCalendarQuickLink: function(num, offset) {
243
+ var d = new Date();
244
+ d.setDate(d.getDate() + offset)
245
+ DateTimeShortcuts.calendarInputs[num].value = d.getISODate();
246
+ DateTimeShortcuts.dismissCalendar(num);
247
+ },
248
+ cancelEventPropagation: function(e) {
249
+ if (!e) e = window.event;
250
+ e.cancelBubble = true;
251
+ if (e.stopPropagation) e.stopPropagation();
252
+ }
253
+ }
254
+
255
+ addEvent(window, 'load', DateTimeShortcuts.init);
@@ -0,0 +1,96 @@
1
+ // Handles related-objects functionality: lookup link for raw_id_fields
2
+ // and Add Another links.
3
+
4
+ function html_unescape(text) {
5
+ // Unescape a string that was escaped using django.utils.html.escape.
6
+ text = text.replace(/&lt;/g, '<');
7
+ text = text.replace(/&gt;/g, '>');
8
+ text = text.replace(/&quot;/g, '"');
9
+ text = text.replace(/&#39;/g, "'");
10
+ text = text.replace(/&amp;/g, '&');
11
+ return text;
12
+ }
13
+
14
+ // IE doesn't accept periods or dashes in the window name, but the element IDs
15
+ // we use to generate popup window names may contain them, therefore we map them
16
+ // to allowed characters in a reversible way so that we can locate the correct
17
+ // element when the popup window is dismissed.
18
+ function id_to_windowname(text) {
19
+ text = text.replace(/\./g, '__dot__');
20
+ text = text.replace(/\-/g, '__dash__');
21
+ return text;
22
+ }
23
+
24
+ function windowname_to_id(text) {
25
+ text = text.replace(/__dot__/g, '.');
26
+ text = text.replace(/__dash__/g, '-');
27
+ return text;
28
+ }
29
+
30
+ function showRelatedObjectLookupPopup(triggeringLink) {
31
+ var name = triggeringLink.id.replace(/^lookup_/, '');
32
+ name = id_to_windowname(name);
33
+ var href;
34
+ if (triggeringLink.href.search(/\?/) >= 0) {
35
+ href = triggeringLink.href + '&pop=1';
36
+ } else {
37
+ href = triggeringLink.href + '?pop=1';
38
+ }
39
+ var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
40
+ win.focus();
41
+ return false;
42
+ }
43
+
44
+ function dismissRelatedLookupPopup(win, chosenId) {
45
+ var name = windowname_to_id(win.name);
46
+ var elem = document.getElementById(name);
47
+ if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
48
+ elem.value += ',' + chosenId;
49
+ } else {
50
+ document.getElementById(name).value = chosenId;
51
+ }
52
+ win.close();
53
+ }
54
+
55
+ function showAddAnotherPopup(triggeringLink) {
56
+ var name = triggeringLink.id.replace(/^add_/, '');
57
+ name = id_to_windowname(name);
58
+ href = triggeringLink.href
59
+ if (href.indexOf('?') == -1) {
60
+ href += '?_popup=1';
61
+ } else {
62
+ href += '&_popup=1';
63
+ }
64
+ var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
65
+ win.focus();
66
+ return false;
67
+ }
68
+
69
+ function dismissAddAnotherPopup(win, newId, newRepr) {
70
+ // newId and newRepr are expected to have previously been escaped by
71
+ // django.utils.html.escape.
72
+ newId = html_unescape(newId);
73
+ newRepr = html_unescape(newRepr);
74
+ var name = windowname_to_id(win.name);
75
+ var elem = document.getElementById(name);
76
+ if (elem) {
77
+ if (elem.nodeName == 'SELECT') {
78
+ var o = new Option(newRepr, newId);
79
+ elem.options[elem.options.length] = o;
80
+ o.selected = true;
81
+ } else if (elem.nodeName == 'INPUT') {
82
+ if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
83
+ elem.value += ',' + newId;
84
+ } else {
85
+ elem.value = newId;
86
+ }
87
+ }
88
+ } else {
89
+ var toId = name + "_to";
90
+ elem = document.getElementById(toId);
91
+ var o = new Option(newRepr, newId);
92
+ SelectBox.add_to_cache(toId, o);
93
+ SelectBox.redisplay(toId);
94
+ }
95
+ win.close();
96
+ }
@@ -0,0 +1,111 @@
1
+ var SelectBox = {
2
+ cache: new Object(),
3
+ init: function(id) {
4
+ var box = document.getElementById(id);
5
+ var node;
6
+ SelectBox.cache[id] = new Array();
7
+ var cache = SelectBox.cache[id];
8
+ for (var i = 0; (node = box.options[i]); i++) {
9
+ cache.push({value: node.value, text: node.text, displayed: 1});
10
+ }
11
+ },
12
+ redisplay: function(id) {
13
+ // Repopulate HTML select box from cache
14
+ var box = document.getElementById(id);
15
+ box.options.length = 0; // clear all options
16
+ for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {
17
+ var node = SelectBox.cache[id][i];
18
+ if (node.displayed) {
19
+ box.options[box.options.length] = new Option(node.text, node.value, false, false);
20
+ }
21
+ }
22
+ },
23
+ filter: function(id, text) {
24
+ // Redisplay the HTML select box, displaying only the choices containing ALL
25
+ // the words in text. (It's an AND search.)
26
+ var tokens = text.toLowerCase().split(/\s+/);
27
+ var node, token;
28
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
29
+ node.displayed = 1;
30
+ for (var j = 0; (token = tokens[j]); j++) {
31
+ if (node.text.toLowerCase().indexOf(token) == -1) {
32
+ node.displayed = 0;
33
+ }
34
+ }
35
+ }
36
+ SelectBox.redisplay(id);
37
+ },
38
+ delete_from_cache: function(id, value) {
39
+ var node, delete_index = null;
40
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
41
+ if (node.value == value) {
42
+ delete_index = i;
43
+ break;
44
+ }
45
+ }
46
+ var j = SelectBox.cache[id].length - 1;
47
+ for (var i = delete_index; i < j; i++) {
48
+ SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
49
+ }
50
+ SelectBox.cache[id].length--;
51
+ },
52
+ add_to_cache: function(id, option) {
53
+ SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
54
+ },
55
+ cache_contains: function(id, value) {
56
+ // Check if an item is contained in the cache
57
+ var node;
58
+ for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
59
+ if (node.value == value) {
60
+ return true;
61
+ }
62
+ }
63
+ return false;
64
+ },
65
+ move: function(from, to) {
66
+ var from_box = document.getElementById(from);
67
+ var to_box = document.getElementById(to);
68
+ var option;
69
+ for (var i = 0; (option = from_box.options[i]); i++) {
70
+ if (option.selected && SelectBox.cache_contains(from, option.value)) {
71
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
72
+ SelectBox.delete_from_cache(from, option.value);
73
+ }
74
+ }
75
+ SelectBox.redisplay(from);
76
+ SelectBox.redisplay(to);
77
+ },
78
+ move_all: function(from, to) {
79
+ var from_box = document.getElementById(from);
80
+ var to_box = document.getElementById(to);
81
+ var option;
82
+ for (var i = 0; (option = from_box.options[i]); i++) {
83
+ if (SelectBox.cache_contains(from, option.value)) {
84
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
85
+ SelectBox.delete_from_cache(from, option.value);
86
+ }
87
+ }
88
+ SelectBox.redisplay(from);
89
+ SelectBox.redisplay(to);
90
+ },
91
+ sort: function(id) {
92
+ SelectBox.cache[id].sort( function(a, b) {
93
+ a = a.text.toLowerCase();
94
+ b = b.text.toLowerCase();
95
+ try {
96
+ if (a > b) return 1;
97
+ if (a < b) return -1;
98
+ }
99
+ catch (e) {
100
+ // silently fail on IE 'unknown' exception
101
+ }
102
+ return 0;
103
+ } );
104
+ },
105
+ select_all: function(id) {
106
+ var box = document.getElementById(id);
107
+ for (var i = 0; i < box.options.length; i++) {
108
+ box.options[i].selected = 'selected';
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,113 @@
1
+ /*
2
+ SelectFilter2 - Turns a multiple-select box into a filter interface.
3
+
4
+ Different than SelectFilter because this is coupled to the admin framework.
5
+
6
+ Requires core.js, SelectBox.js and addevent.js.
7
+ */
8
+
9
+ function findForm(node) {
10
+ // returns the node of the form containing the given node
11
+ if (node.tagName.toLowerCase() != 'form') {
12
+ return findForm(node.parentNode);
13
+ }
14
+ return node;
15
+ }
16
+
17
+ var SelectFilter = {
18
+ init: function(field_id, field_name, is_stacked, admin_media_prefix) {
19
+ var from_box = document.getElementById(field_id);
20
+ from_box.id += '_from'; // change its ID
21
+ from_box.className = 'filtered';
22
+
23
+ // Remove <p class="info">, because it just gets in the way.
24
+ var ps = from_box.parentNode.getElementsByTagName('p');
25
+ for (var i=0; i<ps.length; i++) {
26
+ from_box.parentNode.removeChild(ps[i]);
27
+ }
28
+
29
+ // <div class="selector"> or <div class="selector stacked">
30
+ var selector_div = quickElement('div', from_box.parentNode);
31
+ selector_div.className = is_stacked ? 'selector stacked' : 'selector';
32
+
33
+ // <div class="selector-available">
34
+ var selector_available = quickElement('div', selector_div, '');
35
+ selector_available.className = 'selector-available';
36
+ quickElement('h2', selector_available, interpolate(gettext('Available %s'), [field_name]));
37
+ var filter_p = quickElement('p', selector_available, '');
38
+ filter_p.className = 'selector-filter';
39
+ quickElement('img', filter_p, '', 'src', admin_media_prefix + '/selector-search.gif');
40
+ filter_p.appendChild(document.createTextNode(' '));
41
+ var filter_input = quickElement('input', filter_p, '', 'type', 'text');
42
+ filter_input.id = field_id + '_input';
43
+ selector_available.appendChild(from_box);
44
+ var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); })()');
45
+ choose_all.className = 'selector-chooseall';
46
+
47
+ // <ul class="selector-chooser">
48
+ var selector_chooser = quickElement('ul', selector_div, '');
49
+ selector_chooser.className = 'selector-chooser';
50
+ var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Add'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to");})()');
51
+ add_link.className = 'selector-add';
52
+ var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from");})()');
53
+ remove_link.className = 'selector-remove';
54
+
55
+ // <div class="selector-chosen">
56
+ var selector_chosen = quickElement('div', selector_div, '');
57
+ selector_chosen.className = 'selector-chosen';
58
+ quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s'), [field_name]));
59
+ var selector_filter = quickElement('p', selector_chosen, gettext('Select your choice(s) and click '));
60
+ selector_filter.className = 'selector-filter';
61
+ quickElement('img', selector_filter, '', 'src', admin_media_prefix + (is_stacked ? '/selector_stacked-add.gif':'/selector-add.gif'), 'alt', 'Add');
62
+ var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
63
+ to_box.className = 'filtered';
64
+ var clear_all = quickElement('a', selector_chosen, gettext('Clear all'), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from");})()');
65
+ clear_all.className = 'selector-clearall';
66
+
67
+ from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
68
+
69
+ // Set up the JavaScript event handlers for the select box filter interface
70
+ addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
71
+ addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
72
+ addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });
73
+ addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });
74
+ addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
75
+ SelectBox.init(field_id + '_from');
76
+ SelectBox.init(field_id + '_to');
77
+ // Move selected from_box options to to_box
78
+ SelectBox.move(field_id + '_from', field_id + '_to');
79
+ },
80
+ filter_key_up: function(event, field_id) {
81
+ from = document.getElementById(field_id + '_from');
82
+ // don't submit form if user pressed Enter
83
+ if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
84
+ from.selectedIndex = 0;
85
+ SelectBox.move(field_id + '_from', field_id + '_to');
86
+ from.selectedIndex = 0;
87
+ return false;
88
+ }
89
+ var temp = from.selectedIndex;
90
+ SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
91
+ from.selectedIndex = temp;
92
+ return true;
93
+ },
94
+ filter_key_down: function(event, field_id) {
95
+ from = document.getElementById(field_id + '_from');
96
+ // right arrow -- move across
97
+ if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
98
+ var old_index = from.selectedIndex;
99
+ SelectBox.move(field_id + '_from', field_id + '_to');
100
+ from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
101
+ return false;
102
+ }
103
+ // down arrow -- wrap around
104
+ if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
105
+ from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
106
+ }
107
+ // up arrow -- wrap around
108
+ if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
109
+ from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
110
+ }
111
+ return true;
112
+ }
113
+ }
@@ -0,0 +1,39 @@
1
+ var Actions = {
2
+ init: function() {
3
+ var selectAll = document.getElementById('action-toggle');
4
+ if (selectAll) {
5
+ selectAll.style.display = 'inline';
6
+ addEvent(selectAll, 'click', function() {
7
+ Actions.checker(selectAll.checked);
8
+ });
9
+ }
10
+ var changelistTable = document.getElementsBySelector('#changelist table')[0];
11
+ if (changelistTable) {
12
+ addEvent(changelistTable, 'click', function(e) {
13
+ if (!e) { var e = window.event; }
14
+ var target = e.target ? e.target : e.srcElement;
15
+ if (target.nodeType == 3) { target = target.parentNode; }
16
+ if (target.className == 'action-select') {
17
+ var tr = target.parentNode.parentNode;
18
+ Actions.toggleRow(tr, target.checked);
19
+ }
20
+ });
21
+ }
22
+ },
23
+ toggleRow: function(tr, checked) {
24
+ if (checked && tr.className.indexOf('selected') == -1) {
25
+ tr.className += ' selected';
26
+ } else if (!checked) {
27
+ tr.className = tr.className.replace(' selected', '');
28
+ }
29
+ },
30
+ checker: function(checked) {
31
+ var actionCheckboxes = document.getElementsBySelector('tr input.action-select');
32
+ for(var i = 0; i < actionCheckboxes.length; i++) {
33
+ actionCheckboxes[i].checked = checked;
34
+ Actions.toggleRow(actionCheckboxes[i].parentNode.parentNode, checked);
35
+ }
36
+ }
37
+ };
38
+
39
+ addEvent(window, 'load', Actions.init);