merb-admin 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.markdown +58 -0
- data/Rakefile +64 -0
- data/app/controllers/application.rb +6 -0
- data/app/controllers/main.rb +145 -0
- data/app/helpers/application_helper.rb +64 -0
- data/app/helpers/main_helper.rb +122 -0
- data/app/views/layout/_message.html.erb +10 -0
- data/app/views/layout/dashboard.html.erb +34 -0
- data/app/views/layout/form.html.erb +48 -0
- data/app/views/layout/list.html.erb +42 -0
- data/app/views/main/_belongs_to.html.erb +29 -0
- data/app/views/main/_big_decimal.html.erb +12 -0
- data/app/views/main/_boolean.html.erb +7 -0
- data/app/views/main/_date.html.erb +12 -0
- data/app/views/main/_datetime.html.erb +12 -0
- data/app/views/main/_float.html.erb +12 -0
- data/app/views/main/_has_many.html.erb +16 -0
- data/app/views/main/_has_one.html.erb +30 -0
- data/app/views/main/_integer.html.erb +18 -0
- data/app/views/main/_properties.html.erb +18 -0
- data/app/views/main/_string.html.erb +12 -0
- data/app/views/main/_text.html.erb +11 -0
- data/app/views/main/_time.html.erb +12 -0
- data/app/views/main/_timestamp.html.erb +12 -0
- data/app/views/main/delete.html.erb +12 -0
- data/app/views/main/edit.html.erb +25 -0
- data/app/views/main/index.html.erb +22 -0
- data/app/views/main/list.html.erb +72 -0
- data/app/views/main/new.html.erb +22 -0
- data/lib/abstract_model.rb +67 -0
- data/lib/activerecord_support.rb +169 -0
- data/lib/datamapper_support.rb +157 -0
- data/lib/generic_support.rb +17 -0
- data/lib/merb-admin/merbtasks.rb +103 -0
- data/lib/merb-admin/slicetasks.rb +20 -0
- data/lib/merb-admin/spectasks.rb +53 -0
- data/lib/merb-admin.rb +99 -0
- data/public/images/arrow-down.gif +0 -0
- data/public/images/arrow-up.gif +0 -0
- data/public/images/changelist-bg.gif +0 -0
- data/public/images/changelist-bg_rtl.gif +0 -0
- data/public/images/chooser-bg.gif +0 -0
- data/public/images/chooser_stacked-bg.gif +0 -0
- data/public/images/default-bg-reverse.gif +0 -0
- data/public/images/default-bg.gif +0 -0
- data/public/images/deleted-overlay.gif +0 -0
- data/public/images/icon-no.gif +0 -0
- data/public/images/icon-unknown.gif +0 -0
- data/public/images/icon-yes.gif +0 -0
- data/public/images/icon_addlink.gif +0 -0
- data/public/images/icon_alert.gif +0 -0
- data/public/images/icon_calendar.gif +0 -0
- data/public/images/icon_changelink.gif +0 -0
- data/public/images/icon_clock.gif +0 -0
- data/public/images/icon_deletelink.gif +0 -0
- data/public/images/icon_error.gif +0 -0
- data/public/images/icon_searchbox.png +0 -0
- data/public/images/icon_success.gif +0 -0
- data/public/images/inline-delete-8bit.png +0 -0
- data/public/images/inline-delete.png +0 -0
- data/public/images/inline-restore-8bit.png +0 -0
- data/public/images/inline-restore.png +0 -0
- data/public/images/inline-splitter-bg.gif +0 -0
- data/public/images/nav-bg-grabber.gif +0 -0
- data/public/images/nav-bg-reverse.gif +0 -0
- data/public/images/nav-bg.gif +0 -0
- data/public/images/selector-add.gif +0 -0
- data/public/images/selector-addall.gif +0 -0
- data/public/images/selector-remove.gif +0 -0
- data/public/images/selector-removeall.gif +0 -0
- data/public/images/selector-search.gif +0 -0
- data/public/images/selector_stacked-add.gif +0 -0
- data/public/images/selector_stacked-remove.gif +0 -0
- data/public/images/tool-left.gif +0 -0
- data/public/images/tool-left_over.gif +0 -0
- data/public/images/tool-right.gif +0 -0
- data/public/images/tool-right_over.gif +0 -0
- data/public/images/tooltag-add.gif +0 -0
- data/public/images/tooltag-add_over.gif +0 -0
- data/public/images/tooltag-arrowright.gif +0 -0
- data/public/images/tooltag-arrowright_over.gif +0 -0
- data/public/javascripts/CollapsedFieldsets.js +85 -0
- data/public/javascripts/DateTimeShortcuts.js +255 -0
- data/public/javascripts/RelatedObjectLookups.js +96 -0
- data/public/javascripts/SelectBox.js +111 -0
- data/public/javascripts/SelectFilter2.js +113 -0
- data/public/javascripts/actions.js +39 -0
- data/public/javascripts/calendar.js +143 -0
- data/public/javascripts/core.js +176 -0
- data/public/javascripts/dateparse.js +233 -0
- data/public/javascripts/getElementsBySelector.js +167 -0
- data/public/javascripts/i18n.js +33 -0
- data/public/javascripts/master.js +0 -0
- data/public/javascripts/ordering.js +137 -0
- data/public/javascripts/timeparse.js +94 -0
- data/public/javascripts/urlify.js +140 -0
- data/public/stylesheets/base.css +746 -0
- data/public/stylesheets/changelists.css +269 -0
- data/public/stylesheets/dashboard.css +24 -0
- data/public/stylesheets/forms.css +327 -0
- data/public/stylesheets/global.css +142 -0
- data/public/stylesheets/ie.css +51 -0
- data/public/stylesheets/layout.css +29 -0
- data/public/stylesheets/login.css +54 -0
- data/public/stylesheets/master.css +2 -0
- data/public/stylesheets/null.css +1 -0
- data/public/stylesheets/patch-iewin.css +8 -0
- data/public/stylesheets/rtl.css +206 -0
- data/public/stylesheets/widgets.css +506 -0
- data/schema/migrations/001_create_divisions_migration.rb +15 -0
- data/schema/migrations/002_create_drafts_migration.rb +21 -0
- data/schema/migrations/003_create_leagues_migration.rb +13 -0
- data/schema/migrations/004_create_players_migration.rb +23 -0
- data/schema/migrations/005_create_teams_migration.rb +18 -0
- data/spec/controllers/main_spec.rb +25 -0
- data/spec/models/activerecord/division.rb +8 -0
- data/spec/models/activerecord/draft.rb +16 -0
- data/spec/models/activerecord/league.rb +6 -0
- data/spec/models/activerecord/player.rb +10 -0
- data/spec/models/activerecord/team.rb +11 -0
- data/spec/models/datamapper/division.rb +12 -0
- data/spec/models/datamapper/draft.rb +18 -0
- data/spec/models/datamapper/league.rb +11 -0
- data/spec/models/datamapper/player.rb +21 -0
- data/spec/models/datamapper/team.rb +15 -0
- data/spec/requests/main_spec.rb +475 -0
- data/spec/spec_helper.rb +112 -0
- 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">‹</a>
|
|
150
|
+
// <a href="#" class="link-next">›</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(/</g, '<');
|
|
7
|
+
text = text.replace(/>/g, '>');
|
|
8
|
+
text = text.replace(/"/g, '"');
|
|
9
|
+
text = text.replace(/'/g, "'");
|
|
10
|
+
text = text.replace(/&/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);
|