]> err.no Git - sope/blob - sope-appserver/WEExtensions/WECalendarField.m
added strict OSX bundle dependencies
[sope] / sope-appserver / WEExtensions / WECalendarField.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WECalendarField.h"
23 #include <NGObjWeb/WEClientCapabilities.h>
24 #include "common.h"
25
26 static Class StrClass = Nil;
27
28 @implementation WECalendarField
29
30 + (int)version {
31   return [super version] + 0 /* v2 */;
32 }
33
34 + (void)initialize {
35   NSAssert2([super version] == 2,
36             @"invalid superclass (%@) version %i !",
37             NSStringFromClass([self superclass]), [super version]);
38   StrClass = [NSString class];
39 }
40
41 static NSString *retStrForInt(int i) {
42   // TODO: find out good statics
43   return [[StrClass alloc] initWithFormat:@"%i", i];
44 }
45 static NSString *retStr02ForInt(int i) {
46   switch (i) { // TODO: find out a good count ...
47   case 0:  return @"00";
48   case 1:  return @"01";
49   case 2:  return @"02";
50   case 3:  return @"03";
51   case 4:  return @"04";
52   case 5:  return @"05";
53   case 6:  return @"06";
54   case 7:  return @"07";
55   case 8:  return @"08";
56   case 9:  return @"09";
57   default: 
58     // TODO: add log ...
59     return [[StrClass alloc] initWithFormat:@"%02i", i];
60   }
61 }
62
63 - (id)initWithName:(NSString *)_name
64   associations:(NSDictionary *)_config
65   template:(WOElement *)_subs
66 {
67   if ((self = [super initWithName:_name associations: _config template:_subs])) {
68     self->name           = OWGetProperty(_config, @"name");
69     self->date           = OWGetProperty(_config, @"date");
70     
71     // time field associations
72     self->hour           = OWGetProperty(_config, @"hour");
73     self->minute         = OWGetProperty(_config, @"minute");
74     self->second         = OWGetProperty(_config, @"second");
75     self->useTextField   = OWGetProperty(_config, @"useTextField");
76     self->hourInterval   = OWGetProperty(_config, @"hourInterval");
77     self->minuteInterval = OWGetProperty(_config, @"minuteInterval");
78     self->secondInterval = OWGetProperty(_config, @"secondInterval");
79
80     // date field associations
81     self->year           = OWGetProperty(_config, @"year");
82     self->month          = OWGetProperty(_config, @"month");
83     self->day            = OWGetProperty(_config, @"day");
84     self->format         = OWGetProperty(_config, @"format");
85
86     self->template = [_subs retain];
87   }
88   return self;
89 }
90
91 - (void)dealloc {
92   [self->name release];
93   [self->date release];
94   
95   /* time field associations */
96   [self->hour           release];
97   [self->minute         release];
98   [self->second         release];
99   [self->useTextField   release];
100   [self->hourInterval   release];
101   [self->minuteInterval release];
102   [self->secondInterval release];
103
104   /* date field associations */
105   [self->year   release];
106   [self->month  release];
107   [self->day    release];
108   [self->format release];
109   
110   [self->template release];
111   [super dealloc];
112 }
113
114 - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
115   [self _takeValuesFromTimeFieldRequest:_rq inContext:_ctx];
116   [self _takeValuesFromDateFieldRequest:_rq inContext:_ctx];
117 }
118
119 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
120   id result = nil;
121
122   result = [self _invokeActionForTimeFieldRequest:_rq inContext:_ctx];
123   if (result == nil)
124     result = [self _invokeActionForDateFieldRequest:_rq inContext:_ctx];
125   
126   return result;
127 }
128
129 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
130   [_response appendContentString:
131              @"<table border='0' cellpadding='0' cellspacing='0'>"
132              @"<tr>"
133              @"<td align='left' valign='bottom'>"];
134   [self _appendTimeFieldToResponse:_response inContext:_ctx];
135   
136   [_response appendContentString:
137              @"</td>"
138              @"<td align='left' valign='bottom'>"];
139   
140   [self _appendDateFieldToResponse:_response inContext:_ctx];
141
142   [_response appendContentString:
143              @"</td>"
144              @"</tr>"
145              @"</table>"];
146 }
147
148 @end /* WECalendarField */
149
150 @implementation WECalendarField(WETimeFieldImplementation)
151
152 /* Private Methodes */
153
154 - (NSString *)_divIDAndScriptInContext:(WOContext *)_ctx
155   response:(WOResponse *)_response
156 {
157   int divCount;
158   
159   divCount = [[_ctx valueForKey: @"WETimeFieldScript"] intValue];
160   if (divCount == 0) {
161     [_response appendContentString:
162       @"<style type=\"text/css\">\n"
163       @"<!--\n"
164       @"A.DDLlink { width: 23px; font: normal 10pt Arial; color: "
165       @"#6F1537; text-decoration: none; } \n"
166       @"A.DDLlink:hover { color: red; background: #FAE8B8; } \n"
167       @"//-->\n"
168       @"</style>"];
169     [_response appendContentString:
170       @"<script language=\"JavaScript\">\n"
171       @"<!--\n"
172       @"var DDLlayerCount = 1000;\n"
173       @"function DDLopen(layerObj,el) {\n"
174       @"  if (layerObj.style.visibility == 'hidden') {\n"
175       @"    layerObj.style.visibility = 'visible';\n"
176       @"    layerObj.style.zIndex     = DDLlayerCount;\n"
177       @"    formObj = DDLformField(el);\n"
178       @"    formObj.contentEditable = false;\n"
179       @"    DDLlayerCount++;\n"
180       @"    "
181       @"  } else { layerObj.style.visibility = 'hidden'; }\n"
182       @"}\n\n"
183       @"function DDLreturn(layerObj,el,value) {\n"
184       @"  formObj = DDLformField(el);\n"
185       @"  formObj.value = value;\n"
186       @"  formObj.contentEditable = true;\n"
187       @"  layerObj.style.visibility = 'hidden';\n"
188       @"}\n"
189       @"function DDLformField(el) {\n"
190       @"  for (i = 0; i < document.forms.length; i++)\n"
191       @"    for (j = 0; j < document.forms[i].elements.length; j++)\n"
192       @"      if (document.forms[i].elements[j].name == el)\n"
193       @"        return document.forms[i].elements[j];\n"
194       @"}\n"
195       @"//-->\n"
196       @"</script>"];
197   }
198   [_ctx takeValue:[NSNumber numberWithInt:(divCount+1)]
199         forKey:@"WETimeFieldScript"];
200   
201   return [StrClass stringWithFormat:@"dropDownDiv%i", divCount];
202 }
203
204 - (void)_appendSelectToResponse:(WOResponse *)_response
205   inContext:(WOContext *)_ctx
206   elementIDComponent:(NSString *)_elementIDComponent
207   count:(int)_count
208   selectedIndex:(int)_idx
209   interval:(int)_interval
210 {
211   WEClientCapabilities *ccaps;
212   NSString *tmp;
213   int      i;
214   NSString *userAgent;
215   BOOL     isMSIE;
216   NSString *divID;
217   NSString *img;
218   NSString *elementId;
219
220   ccaps     = [[_ctx request] clientCapabilities];
221   userAgent = [[_ctx request] headerForKey: @"user-agent"];
222   isMSIE    = [ccaps isInternetExplorer];
223   elementId = [self elementIdWithSuffix:_elementIDComponent ctx:_ctx];
224   
225   divID = [self _divIDAndScriptInContext:_ctx response:_response];
226   
227   img = WEUriOfResource(@"downstairs.gif", _ctx);
228
229   if (isMSIE && img) {
230     NSString *s;
231     
232     [_response appendContentString:@"<input readonly=\"readonly\" name=\""];
233     [_response appendContentString:elementId];
234     [_response appendContentString:@"\" value=\""];
235
236     s = retStr02ForInt(_idx);
237     [_response appendContentHTMLAttributeValue:s];
238     [s release];
239     
240     [_response appendContentString:
241                  @"\" type=\"text\" size=\"2\" maxlength=\"2\""];
242 #if 0
243     [_response appendContentString:@" style=\"background-color: #FFDAAA;\""];
244 #endif
245     [_response appendContentString:@" /><img border=\"0\" src=\""];
246     [_response appendContentString:img];
247     [_response appendContentString:@"\" onClick=\""];
248     [_response appendContentString:@"javascript:DDLopen("];
249     [_response appendContentString:divID];
250     [_response appendContentString:@",'"];
251     [_response appendContentString:elementId];
252     [_response appendContentString:@"')\" /><br />"];
253     [_response appendContentString:@"<div id=\""];
254     [_response appendContentString:divID];
255     [_response appendContentString:
256       @"\" style=\"position: absolute; overflow: auto; height: 150; width: 47;"
257       @" background: #FFDAAA; border: 1 solid; "
258       @"visibility: hidden; padding: 0 0 0 2;\">"];
259   }
260   else {
261     [_response appendContentString:@"<select name=\""];
262     [_response appendContentString:elementId];
263     [_response appendContentString:@"\">"];
264   }
265   
266   for (i = 0; i <= _count; i += _interval) {
267     tmp = retStr02ForInt(i);
268     if (isMSIE && img) {
269       NSString *s;
270
271       s = [[StrClass alloc] initWithFormat:
272          @"<a class=\"DDLlink\" href=\"javascript:DDLreturn(%@,'%@','%@')\">%@"
273          @"</a><br />",
274                     divID, elementId, tmp, tmp];
275       [_response appendContentString:s];
276       [s release];
277     }
278     else {
279       [_response appendContentString:@"<option value=\""];
280       [_response appendContentString:tmp];
281       [_response appendContentString:@"\""];
282       [_response appendContentString:
283                    (i == _idx) ? @" selected=\"selected\"" : @""];
284       [_response appendContentString:@">"];
285       [_response appendContentString:tmp];
286       [_response appendContentString:@"</option>"];
287     }
288     [tmp release]; tmp = nil;
289   }
290
291   if (isMSIE && img)
292     [_response appendContentString:@"</div>"];
293   else
294     [_response appendContentString:@"</select>"];
295 }
296
297 /* handle request */
298
299 - (void)_takeValuesFromTimeFieldRequest:(WORequest *)_request
300   inContext:(WOContext *)_ctx
301 {
302   WOComponent *comp;
303   id          formValue;
304   BOOL        tuseTField;
305   NSString    *elementId;
306   NSArray     *ta;
307   int         idx;
308   
309   comp = [_ctx component];
310   tuseTField = self->useTextField
311     ? [[self->useTextField valueInComponent: comp] boolValue]
312     : NO;
313   
314   if (tuseTField) {
315     // TextField value
316     elementId = [self elementIdWithSuffix:@"" ctx:_ctx];
317     if ((formValue = [_request formValueForKey:elementId])) {
318       int intValue, cnt;
319       
320       ta = [formValue componentsSeparatedByString:@":"];
321       cnt = [ta count];
322       
323       idx = 0;
324       if ([self isHourSettable]) {
325         intValue = (idx < cnt) ? [[ta objectAtIndex:idx] intValue] : 0;
326         [self setHour:intValue inComponent:comp];
327         idx++;
328       }
329       if ([self isMinuteSettable]) {
330         intValue = (idx < cnt) ? [[ta objectAtIndex:idx] intValue] : 0;
331         [self setMinute:intValue inComponent:comp];
332         idx++;
333       }
334       if ([self isSecondSettable]) {
335         intValue = (idx < cnt) ? [[ta objectAtIndex:idx] intValue] : 0;        
336         [self setSecond:intValue inComponent:comp];
337       }
338     }
339   }
340   else {
341     elementId = [self elementIdWithSuffix:@"hour" ctx:_ctx];
342     if ((formValue = [_request formValueForKey:elementId])) {
343       if ([self isHourSettable])
344         [self setHour:[formValue intValue] inComponent:comp];
345     }
346
347     elementId = [self elementIdWithSuffix:@"minute" ctx:_ctx];
348     if ((formValue = [_request formValueForKey:elementId])) {
349       if ([self isMinuteSettable])
350         [self setMinute:[formValue intValue] inComponent:comp];
351     }
352
353     elementId = [self elementIdWithSuffix:@"second" ctx:_ctx];
354     if ((formValue = [_request formValueForKey:elementId])) {
355       if ([self isSecondSettable])
356         [self setSecond:[formValue intValue] inComponent:comp];
357     }
358   }
359   
360   /* template */
361   [_ctx appendElementIDComponent:@"timeField"];
362   [self->template takeValuesFromRequest:_request inContext:_ctx];
363   [_ctx deleteLastElementIDComponent];
364 }
365
366 - (id)_invokeActionForTimeFieldRequest:(WORequest *)_rq
367   inContext:(WOContext *)_ctx
368 {
369   return [self->template invokeActionForRequest:_rq inContext:_ctx];
370 }
371
372 /* generate response */
373
374 - (void)_appendTimeFieldToResponse:(WOResponse *)_r inContext:(WOContext *)_cx{
375   NSCalendarDate *tdate;
376   WOComponent    *comp;
377   BOOL           tuseTField;
378   int            hourInt;        // hourInterval
379   int            minuteInt;      // minuteInterval
380   int            secondInt;      // secondInterval
381   NSMutableArray *ta;
382
383   comp  = [_cx component];
384   tdate = [NSCalendarDate calendarDate];
385
386   hourInt = self->hourInterval
387     ? [self->hourInterval intValueInComponent:comp] : 1;
388
389   minuteInt = self->minuteInterval
390     ? [self->minuteInterval intValueInComponent:comp] : 1;
391
392   secondInt = self->secondInterval
393     ? [self->secondInterval intValueInComponent:comp] : 1;
394
395   tuseTField = self->useTextField
396     ? [self->useTextField boolValueInComponent:comp]
397     : NO;
398   
399   // template
400   [_cx appendElementIDComponent:@"timeField"];
401   [self->template appendToResponse:_r inContext:_cx];
402   [_cx deleteLastElementIDComponent];
403   
404   // all values in one textField
405   if (tuseTField) {
406     int h, m, s;
407     NSString *tmp, *fmt;
408     // build string of values and @":"
409
410     h = [self   hourInComponent:comp];
411     m = [self minuteInComponent:comp];
412     s = [self secondInComponent:comp];
413     
414     ta = [[NSMutableArray alloc] initWithCapacity: 3];
415     fmt = @"%02i";
416     
417     if ([self hasHourInComponent:comp]) {
418       tmp = retStr02ForInt(h);
419       [ta addObject:tmp];
420       [tmp release];
421     }
422     if ([self hasMinuteInComponent:comp]) {
423       tmp = [[StrClass alloc] initWithFormat:fmt, m];
424       [ta addObject:tmp];
425       [tmp release];
426     }
427     if ([self hasSecondInComponent:comp]) {
428       tmp = [[StrClass alloc] initWithFormat:fmt, s];
429       [ta addObject:tmp];
430       [tmp release];
431     }
432     
433     tmp = [ta componentsJoinedByString:@":"];
434     [ta release]; ta = nil;
435
436     /* append to response */
437     if ([tmp length] > 0) {
438       [_r appendContentString:@"<input type=\"text\" name=\""];
439       [_r appendContentString:[self elementIdWithSuffix:@"" ctx:_cx]];
440       [_r appendContentString:@"\" value=\""];
441       [_r appendContentString:tmp];
442       [_r appendContentString:@"\""];
443       
444       tmp = retStrForInt([tmp length]);
445       [_r appendContentString:@" size=\""];
446       [_r appendContentString:tmp];
447       [_r appendContentString:@"\" maxlength=\""];
448       [_r appendContentString:tmp];
449       [_r appendContentString:@"\""];
450       [tmp release];
451       
452       [_r appendContentString:@" />"];
453     }
454   }
455   else {
456     // hour select field
457     [_r appendContentString:
458           @"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>"];
459
460     if ([self hasHourInComponent:comp]) {
461       [_r appendContentString:@"<td valign=\"bottom\">"];
462       [self _appendSelectToResponse:_r inContext:_cx
463             elementIDComponent:@"hour"
464             count:23 selectedIndex:[self hourInComponent:comp]
465             interval:hourInt];
466       [_r appendContentString:@"</td>"];
467     }
468      
469     // minute select field
470     if ([self hasMinuteInComponent:comp]) {
471       [_r appendContentString:@"<td valign=\"bottom\">"];
472       [self _appendSelectToResponse:_r inContext:_cx 
473             elementIDComponent:@"minute"
474             count:59 selectedIndex:[self minuteInComponent:comp]
475             interval:minuteInt];
476       [_r appendContentString:@"</td>"];
477     }
478     
479     // second select field
480     if ([self hasSecondInComponent:comp]) {
481       [_r appendContentString:@"<td valign=\"bottom\">"];
482       [self _appendSelectToResponse:_r inContext:_cx
483             elementIDComponent:@"second"
484             count:59 selectedIndex:[self secondInComponent:comp]
485             interval:secondInt];
486       [_r appendContentString:@"</td>"];
487     }
488     [_r appendContentString:@"</tr></table>"];
489
490   } /* end of (!tuseTField) */
491 }
492
493 @end /* WECalendarField(WETimeFieldImplementation */
494
495 @implementation WECalendarField(WEDateFieldImplementation)
496
497 - (void)_takeValuesFromDateFieldRequest:(WORequest *)_request
498   inContext:(WOContext *)_ctx
499 {
500   NSString       *tformat;
501   NSCalendarDate *tdate;
502   int            tyear;
503   int            tmonth;
504   int            tday;
505   WOComponent    *comp;
506   NSString       *elementId;
507   
508   comp = [_ctx component];
509
510   tformat = [self->format stringValueInComponent: comp];
511   if (tformat == nil) tformat = @"%Y-%m-%d";
512   elementId = [self elementIdWithSuffix:@"" ctx:_ctx];
513
514   if ([self isKindOfClass:[WECalendarField class]]) {
515     id t = tformat;
516     
517     t = [[t componentsSeparatedByString:@"%H"] componentsJoinedByString:@""];
518     t = [[t componentsSeparatedByString:@"%M"] componentsJoinedByString:@""];
519     t = [[t componentsSeparatedByString:@"%S"] componentsJoinedByString:@""];
520
521     tformat = t;
522   }
523   
524   tdate = [NSCalendarDate dateWithString:
525                           [_request formValueForKey:elementId]
526                           calendarFormat: tformat];
527   if (tdate == nil) {
528     NSLog(@"WARNING: WEDateField: field value and format do not match!");
529   }
530   else {
531     tyear  = [tdate yearOfCommonEra];
532     tmonth = [tdate monthOfYear];
533     tday   = [tdate dayOfMonth];
534
535
536     if ([self isYearSettable])  [self  setYear:tyear  inComponent:comp];
537     if ([self isMonthSettable]) [self setMonth:tmonth inComponent:comp];
538     if ([self isDaySettable])   [self   setDay:tday   inComponent:comp];
539   }
540
541   [_ctx appendElementIDComponent:@"dateField"];
542   [self->template takeValuesFromRequest:_request inContext:_ctx];
543   [_ctx deleteLastElementIDComponent];
544 }
545
546 - (id)_invokeActionForDateFieldRequest:(WORequest *)_request
547   inContext:(WOContext *)_ctx
548 {
549   return [self->template invokeActionForRequest:_request inContext:_ctx];
550 }
551
552 - (void)_appendDateFieldToResponse:(WOResponse *)_response
553   inContext:(WOContext *)_ctx
554 {
555   WEClientCapabilities *ccaps;
556   int            tyear;
557   int            tmonth;
558   int            tday;
559   NSString       *tformat;
560   NSString       *tmp;
561   WOComponent    *comp;
562   NSCalendarDate *tdate;
563   NSString       *calendarDivID;
564   int            calendarID;
565   BOOL           isMSIE;
566   NSString       *value;
567
568   ccaps  = [[_ctx request] clientCapabilities];
569   comp   = [_ctx component];
570   tdate  = [NSCalendarDate calendarDate];
571   isMSIE = [ccaps isInternetExplorer];
572
573   if (![_ctx valueForKey:@"WEDateFieldScriptDone"])
574     [WEDateFieldScript appendWEDateFieldScriptToResponse: _response
575       inContext: _ctx
576       headBackground: HEAD_BACKGROUND_COLOR
577       headColor:      HEAD_COLOR
578       headNavColor:   HEAD_NAVIGATION_COLOR
579       labels:         nil
580       useImages:      NO];
581
582   // associations
583   tyear = ([self hasYearInComponent:comp])
584     ? [self yearInComponent:comp]
585     : [tdate yearOfCommonEra];
586   tmonth = ([self hasMonthInComponent:comp])
587     ? [self monthInComponent:comp]
588     : [tdate monthOfYear];
589   tday = ([self hasDayInComponent:comp])
590     ? [self dayInComponent:comp]
591     : [tdate dayOfMonth];
592   tformat    = self->format
593     ? [self->format stringValueInComponent: comp]
594     : @"%Y-%m-%d";
595
596   if ([self isKindOfClass:[WECalendarField class]]) {
597     id t = tformat;
598
599     t = [[t componentsSeparatedByString:@"%H"] componentsJoinedByString:@""];
600     t = [[t componentsSeparatedByString:@"%M"] componentsJoinedByString:@""];
601     t = [[t componentsSeparatedByString:@"%S"] componentsJoinedByString:@""];
602
603     tformat = t;
604   }
605
606   // input field value
607   tdate = [NSCalendarDate dateWithYear: tyear
608                           month:        tmonth
609                           day:          tday
610                           hour: 0 minute: 0 second: 0
611                           timeZone: [tdate timeZone]];
612
613   value = [tdate descriptionWithCalendarFormat: tformat];
614
615   // div id for javascript calendar
616   calendarID = ([_ctx valueForKey: @"WEDateField_DivID"])
617      ? [[_ctx valueForKey: @"WEDateField_DivID"] intValue]
618      : 0;
619   calendarDivID = [StrClass stringWithFormat:@"calendarDiv%d", calendarID];
620   calendarID++;
621   [_ctx takeValue:[NSNumber numberWithInt: calendarID]
622         forKey:@"WEDateField_DivID"];
623
624   // template
625   [_ctx appendElementIDComponent:@"dateField"];
626   [self->template appendToResponse:_response inContext:_ctx];
627   [_ctx deleteLastElementIDComponent];
628
629   [_response appendContentString:
630              @"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"];
631   [_response appendContentString:
632              @"<td valign=\"top\" align=\"left\">"];
633
634   // input field
635   [_response appendContentString:@"<input type=\"text\" name=\""];
636   [_response appendContentString:[self elementIdWithSuffix:@"" ctx:_ctx]];
637   [_response appendContentString:@"\" value=\""];
638   [_response appendContentString:value];
639   [_response appendContentString:@"\" size=\""];
640   [_response appendContentString:
641              [[NSNumber numberWithInt: [value length]] stringValue]];
642   [_response appendContentString:@"\" maxlength=\""];
643   [_response appendContentString:
644              [[NSNumber numberWithInt: [value length]] stringValue]];
645   [_response appendContentString:@"\" />"];
646     
647   /* link to calendar panel */
648   [_response appendContentString:  @" <a href=\"javascript:doNothing()\" "];
649   [_response appendContentString: @"onClick=\""];
650   tmp = @"javascript:%@toggleCalendar('%@',%@%@,'%@')";
651   tmp = [[StrClass alloc] initWithFormat: tmp,
652               (isMSIE) ? @"" : @"actPos;",                  // layer position
653               [self elementIdWithSuffix:@"" ctx:_ctx],      // form element
654               (isMSIE) ? @"" : @"document.", calendarDivID, // calendar DIV
655               tformat];                                     // dateFormat
656   [_response appendContentString: tmp];
657   [tmp release];
658   [_response appendContentString: @"\">"];
659   
660   // calendar image
661   tmp = WEUriOfResource(@"icon_popupcalendar.gif", _ctx);
662
663   if (tmp) {
664     tmp = [[StrClass alloc] initWithFormat:
665                               @"<img border=\"0\" src=\"%@\" />", tmp];
666   }
667   else
668     tmp = @"x";
669   
670   [_response appendContentString:tmp];
671   [tmp release];
672
673   [_response appendContentString: @"</a><br />"];
674     
675   /* calendar panel */
676   [_response appendContentString: @"<div id=\""];
677   [_response appendContentString: calendarDivID];
678   [_response appendContentString: @"\" style=\"position: absolute; "];
679   [_response appendContentString: @"visibility: "];
680   [_response appendContentString: (isMSIE) ? @"hidden" : @"hide"];
681   [_response appendContentString: @";\"></div>"];
682
683   [_response appendContentString: @"</td></tr></table>"];
684 }
685
686 @end /* WECalendarField(WEDateFieldImplementation) */
687
688 @implementation WEDateFieldScript
689
690 - (id)initWithName:(NSString *)_name
691    associations:(NSDictionary *)_config
692    template:    (WOElement *)_subs
693 {
694   if ((self = [super initWithName:_name associations: _config template:_subs]))
695   {
696     self->headBackground = OWGetProperty(_config, @"headBackground");
697     self->headColor      = OWGetProperty(_config, @"headColor");
698     self->headNavColor   = OWGetProperty(_config, @"headNavColor");
699     self->labels         = OWGetProperty(_config, @"labels");
700     self->useImages      = OWGetProperty(_config, @"useImages");
701   }
702   return self;
703 }
704
705 - (void)dealloc {
706   [self->useImages      release];
707   [self->headBackground release];
708   [self->headColor      release];
709   [self->headNavColor   release];
710   [self->labels         release];
711   [super dealloc];
712 }
713
714
715 + (void)appendWEDateFieldScriptToResponse: (WOResponse *)_response
716   inContext:      (WOContext *)_ctx
717   headBackground: (NSString *)_hBack
718   headColor:      (NSString *)_hCol
719   headNavColor:   (NSString *)_hNav
720   labels:         (id)_labels
721   useImages:      (BOOL)_useImg
722 {
723   NSString *tmp;
724   NSString *tmon;
725   NSString *tweek;
726   NSString *timg;
727
728   // images...
729   NSString *firstI;
730   NSString *prevI;
731   NSString *todayI;
732   NSString *nextI;
733   NSString *lastI;
734   NSString *closeI;
735   
736   // colors
737   if (_hBack == nil) _hBack = HEAD_BACKGROUND_COLOR;
738   if (_hCol  == nil) _hCol  = HEAD_COLOR;
739   if (_hNav  == nil) _hNav  = HEAD_NAVIGATION_COLOR;
740
741   // months and weekdays
742   if (_labels   == nil) {
743     NSLog(@"WARNING: WEDateFieldScript: undefined variable 'labels'");
744     tmon  = @"var externMonths = false; \n";
745     tweek = @"var externWeekdays = false; \n";
746   }
747   else {
748     tmon = @"var externMonths = new Array("
749            @"\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\","
750            @"\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\"); \n";
751     tmon = [[StrClass alloc] initWithFormat: tmon,
752                      [_labels valueForKey: @"January"],
753                      [_labels valueForKey: @"February"],
754                      [_labels valueForKey: @"March"],
755                      [_labels valueForKey: @"April"],
756                      [_labels valueForKey: @"May"],
757                      [_labels valueForKey: @"June"],
758                      [_labels valueForKey: @"July"],
759                      [_labels valueForKey: @"August"],
760                      [_labels valueForKey: @"September"],
761                      [_labels valueForKey: @"October"],
762                      [_labels valueForKey: @"November"],
763                      [_labels valueForKey: @"December"]
764                      ];
765     tweek = @"var externWeekdays = new Array("
766             @"\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\",\"%@\"); \n";
767     tweek = [[StrClass alloc] initWithFormat: tweek,
768                       [_labels valueForKey: @"SundayAbbrev"],
769                       [_labels valueForKey: @"MondayAbbrev"],
770                       [_labels valueForKey: @"TuesdayAbbrev"],
771                       [_labels valueForKey: @"WednesdayAbbrev"],
772                       [_labels valueForKey: @"ThursdayAbbrev"],
773                       [_labels valueForKey: @"FridayAbbrev"],
774                       [_labels valueForKey: @"SaturdayAbbrev"]
775                       ];
776   }
777   
778   if (![_ctx valueForKey: @"WEDateFieldScriptDone"]) {
779     tmp = @"<style type='text/css'>\n"
780           @"<!--\n"
781           @"TD.heading { text-decoration: none; color: black; "
782             @"font: bold 8pt arial, helvetica; } \n "
783           @"A.focusDay { color: blue; text-decoration: none; "
784             @"font: 8pt arial, helvetica; } \n"
785           @"A.focusDay:hover { color:darkred; text-decoration: none; "
786             @"font: 8pt arial, helvetica; } \n"
787           @"A.weekday { color: blue; text-decoration: none; "
788             @"font: 8pt arial, helvetica; } \n"
789           @"A.weekday:hover { color: darkred; font: 8pt arial, helvetica; } \n"
790           @"A.navMonYear "
791             @"{ color: %@; text-decoration: none; font: 8pt Arial; }\n"
792           @"TD.topCal "
793             @"{ font: 10pt Arial; color: %@; background-color: %@; } \n"
794           @"-->\n"
795           @"</style>\n";
796     tmp = [StrClass stringWithFormat: tmp, _hNav, _hCol, _hBack];
797     [_response appendContentString: tmp];
798
799     [_response appendContentString:
800                @"<script language=\"JavaScript\">\n<!--\n"];
801     [_response appendContentString: @"var dateFormat = \"%Y-%m-%d\"; \n"];
802     [_response appendContentString: tmon];
803     [_response appendContentString: tweek];
804
805     // navigation images
806     // doppelt haelt wirklich besser
807     firstI = WEUriOfResource(@"first.gif", _ctx);
808     prevI  = WEUriOfResource(@"previous.gif", _ctx);
809     todayI = WEUriOfResource(@"non_sorted.gif", _ctx);
810     nextI  = WEUriOfResource(@"next.gif", _ctx);
811     lastI  = WEUriOfResource(@"last.gif", _ctx);
812     closeI = WEUriOfResource(@"icon_unread.gif", _ctx);
813
814     if (_useImg) {
815       NSString *s;
816       
817       timg = @"var %@=new Image(); %@.src='%@';\n"
818              @"var %@=\"<img border='0' name='%@' src='%@' />\";\n";
819       s = [[StrClass alloc] initWithFormat: timg,
820                   @"dateFieldFirst", @"dateFieldFirst", firstI,
821                   @"dateFieldFirstSRC", @"dateFieldFirstImg", firstI];
822       [_response appendContentString:s];
823       [s release];
824       s = [[StrClass alloc] initWithFormat: timg,
825                   @"dateFieldPrevious", @"dateFieldPrevious", prevI,
826                   @"dateFieldPreviousSRC", @"dateFieldPreviousImg", prevI];
827       [_response appendContentString:s];
828       [s release];
829       s = [[StrClass alloc] initWithFormat: timg,
830                   @"dateFieldToday", @"dateFieldToday", todayI,
831                   @"dateFieldTodaySRC", @"dateFieldTodayImg", todayI];
832       [_response appendContentString:s];
833       [s release];
834       s = [[StrClass alloc] initWithFormat: timg,
835                   @"dateFieldNext", @"dateFieldNext", nextI,
836                   @"dateFieldNextSRC", @"dateFieldNextImg", nextI];
837       [_response appendContentString:s];
838       [s release];
839       s = [[StrClass alloc] initWithFormat: timg,
840                   @"dateFieldLast", @"dateFieldLast", lastI,
841                   @"dateFieldLastSRC", @"dateFieldLastImg", lastI];
842       [_response appendContentString:s];
843       [s release];
844       s = [[StrClass alloc] initWithFormat: timg,
845                   @"dateFieldClose", @"dateFieldClose", closeI,
846                   @"dateFieldCloseSRC", @"dateFieldCloseImg", closeI];
847       [_response appendContentString:s];
848       [s release];
849       [_response appendContentString:@"var usesNavImages = true;\n"];
850     }
851     else {
852       [_response appendContentString:@"var dateFieldCloseSRC=\"X\";\n"];
853       [_response appendContentString:@"var dateFieldFirstSRC=\"&lt;&lt;\";\n"];
854       [_response appendContentString:@"var dateFieldPreviousSRC=\"&lt;\";\n"];
855       [_response appendContentString:@"var dateFieldTodaySRC=\"O\";\n"];
856       [_response appendContentString:@"var dateFieldNextSRC=\"&gt;\";\n"];
857       [_response appendContentString:@"var dateFieldLastSRC=\"&gt;&gt;\";\n"];
858       [_response appendContentString:@"var usesNavImages = false;\n"];
859     }
860     
861     tmp =
862 #include "calendar.jsm"
863       ;
864     [_response appendContentString: tmp];
865     [_response appendContentString: @"\n//-->\n</script>"];
866     
867     [_ctx takeValue: [NSNumber numberWithBool: YES]
868           forKey: @"WEDateFieldScriptDone"];
869   }
870
871   [tmon  release];
872   [tweek release];
873 }
874
875 - (void)appendToResponse: (WOResponse *)_response
876   inContext: (WOContext *)_ctx
877 {
878   WOComponent *comp;
879   comp = [_ctx component];
880   
881   [[self class] appendWEDateFieldScriptToResponse: _response inContext:_ctx
882     headBackground: [self->headBackground stringValueInComponent:comp]
883     headColor:      [self->headColor      stringValueInComponent:comp]
884     headNavColor:   [self->headNavColor   stringValueInComponent:comp]
885     labels:         [self->labels               valueInComponent:comp]
886     useImages:      [[self->useImages valueInComponent: comp] boolValue]];
887 }
888
889 @end /* WEDateFieldScript */
890
891 @implementation WECalendarField(Accessors)
892
893 - (NSString *)elementIdWithSuffix:(NSString *)_suffix ctx:(WOContext *)_ctx {
894   NSString *prefix = nil;
895   
896   if ((prefix = [self->name stringValueInComponent:[_ctx component]]) == nil)
897     prefix = [_ctx elementID];
898   
899   if ([_suffix length])
900     prefix = [prefix stringByAppendingString:@"_"];
901   
902   return [prefix stringByAppendingString:_suffix];
903 }
904
905 - (void)setSecond:(int)_second inComponent:(WOComponent *)_comp {
906   if (self->date) {
907     NSCalendarDate *d = [self->date valueInComponent:_comp];
908
909     
910     d = [NSCalendarDate dateWithYear:[d yearOfCommonEra]
911                         month:[d monthOfYear]
912                         day:[d dayOfMonth]
913                         hour:[d hourOfDay]
914                         minute:[d minuteOfHour]
915                         second:_second
916                         timeZone:[d timeZone]];
917     
918     [self->date setValue:d inComponent:_comp];
919   }
920   else {
921     [self->second setIntValue:_second inComponent:_comp];
922   }
923 }
924 - (int)secondInComponent:(WOComponent *)_comp {
925   return (self->date != nil)
926     ? [[self->date valueInComponent:_comp] secondOfMinute]
927     : [self->second intValueInComponent:_comp];
928 }
929
930 - (void)setMinute:(int)_minute inComponent:(WOComponent *)_comp {
931   if (self->date) {
932     NSCalendarDate *d;
933     
934     d = [self->date valueInComponent:_comp];
935     d = [NSCalendarDate dateWithYear:[d yearOfCommonEra]
936                         month:[d monthOfYear]
937                         day:[d dayOfMonth]
938                         hour:[d hourOfDay]
939                         minute:_minute
940                         second:[d secondOfMinute]
941                         timeZone:[d timeZone]];
942     
943     [self->date setValue:d inComponent:_comp];
944   }
945   else {
946     [self->minute setIntValue:_minute inComponent:_comp];
947   }
948 }
949 - (int)minuteInComponent:(WOComponent *)_comp {
950   return (self->date != nil)
951     ? [[self->date valueInComponent:_comp] minuteOfHour]
952     : [self->minute intValueInComponent:_comp];
953 }
954
955 - (void)setHour:(int)_hour inComponent:(WOComponent *)_comp {
956   if (self->date) {
957     NSCalendarDate *d = [self->date valueInComponent:_comp];
958
959     
960     d = [NSCalendarDate dateWithYear:[d yearOfCommonEra]
961                         month:[d monthOfYear]
962                         day:[d dayOfMonth]
963                         hour:_hour
964                         minute:[d minuteOfHour]
965                         second:[d secondOfMinute]
966                         timeZone:[d timeZone]];
967     
968     [self->date setValue:d inComponent:_comp];
969   }
970   else {
971     [self->hour setIntValue:_hour inComponent:_comp];
972   }
973 }
974 - (int)hourInComponent:(WOComponent *)_comp {
975   return (self->date != nil)
976     ? [[self->date valueInComponent:_comp] hourOfDay]
977     : [self->hour intValueInComponent:_comp];
978 }
979
980 - (void)setDay:(int)_day inComponent:(WOComponent *)_comp {
981   if (self->date) {
982     NSCalendarDate *d = [self->date valueInComponent:_comp];
983     
984     d = [NSCalendarDate dateWithYear:[d yearOfCommonEra]
985                         month:[d monthOfYear]
986                         day:_day
987                         hour:[d hourOfDay]
988                         minute:[d minuteOfHour]
989                         second:[d secondOfMinute]
990                         timeZone:[d timeZone]];
991     
992     [self->date setValue:d inComponent:_comp];
993   }
994   else {
995     [self->day setIntValue:_day inComponent:_comp];
996   }
997 }
998 - (int)dayInComponent:(WOComponent *)_comp {
999   return (self->date != nil)
1000     ? [[self->date valueInComponent:_comp] dayOfMonth]
1001     : [self->day intValueInComponent:_comp];
1002 }
1003
1004 - (void)setMonth:(int)_month inComponent:(WOComponent *)_comp {
1005   if (self->date) {
1006     NSCalendarDate *d = [self->date valueInComponent:_comp];
1007     
1008     d = [NSCalendarDate dateWithYear:[d yearOfCommonEra]
1009                         month:_month
1010                         day:[d dayOfMonth]
1011                         hour:[d hourOfDay]
1012                         minute:[d minuteOfHour]
1013                         second:[d secondOfMinute]
1014                         timeZone:[d timeZone]];
1015     
1016     [self->date setValue:d inComponent:_comp];    
1017   }
1018   else {
1019     [self->month setIntValue:_month inComponent:_comp];
1020   }
1021 }
1022 - (int)monthInComponent:(WOComponent *)_comp {
1023    return (self->date != nil)
1024     ? [[self->date valueInComponent:_comp] monthOfYear]
1025     : [self->month intValueInComponent:_comp];
1026 }
1027
1028 - (void)setYear:(int)_year inComponent:(WOComponent *)_comp {
1029   if (self->date) {
1030     NSCalendarDate *d = [self->date valueInComponent:_comp];
1031     
1032     d = [NSCalendarDate dateWithYear:_year
1033                         month:[d monthOfYear]
1034                         day:[d dayOfMonth]
1035                         hour:[d hourOfDay]
1036                         minute:[d minuteOfHour]
1037                         second:[d secondOfMinute]
1038                         timeZone:[d timeZone]];
1039     
1040     [self->date setValue:d inComponent:_comp];        
1041   }
1042   else {
1043     [self->year setIntValue:_year inComponent:_comp];
1044   }
1045 }
1046 - (int)yearInComponent:(WOComponent *)_comp {
1047   return (self->date != nil)
1048     ? [[self->date valueInComponent:_comp] yearOfCommonEra]
1049     : [self->year intValueInComponent:_comp];
1050 }
1051
1052 - (BOOL)isSecondSettable {
1053   return (self->date != nil)
1054     ? [self->date isValueSettable] : [self->second isValueSettable];
1055 }
1056
1057 - (BOOL)isMinuteSettable {
1058   return (self->date != nil)
1059     ? [self->date isValueSettable] : [self->minute isValueSettable];
1060 }
1061
1062 - (BOOL)isHourSettable {
1063   return (self->date != nil)
1064     ? [self->date isValueSettable] : [self->hour isValueSettable];
1065 }
1066
1067 - (BOOL)isDaySettable {
1068   return (self->date != nil)
1069     ? [self->date isValueSettable] : [self->day isValueSettable];
1070 }
1071
1072 - (BOOL)isMonthSettable {
1073   return (self->date != nil)
1074     ? [self->date isValueSettable] : [self->month isValueSettable];
1075 }
1076
1077 - (BOOL)isYearSettable {
1078   return (self->date != nil)
1079     ? [self->date isValueSettable] : [self->year isValueSettable];
1080 }
1081
1082 - (BOOL)_chkDateFormatKey:(NSString *)_s inComponent:(WOComponent *)_comp {
1083   NSString *fmt;
1084   
1085   if (self->format == nil)
1086     return YES;
1087   
1088   fmt = [self->format stringValueInComponent:_comp];
1089   return ([fmt rangeOfString:_s].length > 0) ? YES : NO;
1090 }
1091
1092 - (BOOL)hasSecondInComponent:(WOComponent *)_comp {
1093   return (self->date)
1094     ? [self _chkDateFormatKey:@"S" inComponent:_comp]
1095     : (self->second != nil ? YES : NO);
1096 }
1097
1098 - (BOOL)hasMinuteInComponent:(WOComponent *)_comp {
1099   return (self->date)
1100     ? [self _chkDateFormatKey:@"M" inComponent:_comp]
1101     : (self->minute != nil ? YES : NO);
1102 }
1103
1104 - (BOOL)hasHourInComponent:(WOComponent *)_comp {
1105   return (self->date)
1106     ? [self _chkDateFormatKey:@"H" inComponent:_comp]
1107     : (self->hour != nil ? YES : NO);
1108 }
1109
1110 - (BOOL)hasDayInComponent:(WOComponent *)_comp {
1111   return (self->date)
1112     ? [self _chkDateFormatKey:@"d" inComponent:_comp]
1113     : (self->day != nil ? YES : NO);
1114 }
1115
1116 - (BOOL)hasMonthInComponent:(WOComponent *)_comp {
1117   return (self->date)
1118     ? [self _chkDateFormatKey:@"m" inComponent:_comp]
1119     : (self->month != nil ? YES : NO);
1120 }
1121
1122 - (BOOL)hasYearInComponent:(WOComponent *)_comp {
1123   return (self->date)
1124     ? [self _chkDateFormatKey:@"Y" inComponent:_comp]
1125     : (self->year != nil ? YES : NO);
1126 }
1127
1128 @end /* WECalendarField */