2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "WETabView.h"
23 #include <NGObjWeb/WEClientCapabilities.h>
27 // # define DEBUG_TAKEVALUES 1
32 NSString *WETabView_HEAD = @"WETabView_head";
33 NSString *WETabView_BODY = @"WETabView_body";
34 NSString *WETabView_KEYS = @"WETabView_keys";
35 NSString *WETabView_SCRIPT = @"WETabView_script";
36 NSString *WETabView_ACTIVEKEY = @"WETabView_activekey";
37 NSString *WETabView_COLLECT = @"~tv~";
39 @implementation WETabView
41 static NSNumber *YesNumber;
45 YesNumber = [[NSNumber numberWithBool:YES] retain];
49 return [super version] + 0;
52 - (id)initWithName:(NSString *)_name
53 associations:(NSDictionary *)_config
54 template:(WOElement *)_t
56 if ((self = [super initWithName:_name associations:_config template:_t])) {
57 self->selection = WOExtGetProperty(_config, @"selection");
59 self->bgColor = WOExtGetProperty(_config, @"bgColor");
60 self->nonSelectedBgColor = WOExtGetProperty(_config,@"nonSelectedBgColor");
61 self->leftCornerIcon = WOExtGetProperty(_config, @"leftCornerIcon");
62 self->rightCornerIcon = WOExtGetProperty(_config, @"rightCornerIcon");
64 self->tabIcon = WOExtGetProperty(_config, @"tabIcon");
65 self->leftTabIcon = WOExtGetProperty(_config, @"leftTabIcon");
66 self->selectedTabIcon = WOExtGetProperty(_config, @"selectedTabIcon");
68 self->asBackground = WOExtGetProperty(_config, @"asBackground");
69 self->width = WOExtGetProperty(_config, @"width");
70 self->height = WOExtGetProperty(_config, @"height");
71 self->activeBgColor = WOExtGetProperty(_config, @"activeBgColor");
72 self->inactiveBgColor = WOExtGetProperty(_config, @"inactiveBgColor");
74 self->fontColor = WOExtGetProperty(_config, @"fontColor");
75 self->fontSize = WOExtGetProperty(_config, @"fontSize");
76 self->fontFace = WOExtGetProperty(_config, @"fontFace");
78 self->disabledTabKeys = WOExtGetProperty(_config, @"disabledTabKeys");
80 self->template = [_t retain];
86 [self->disabledTabKeys release];
87 [self->selection release];
88 [self->bgColor release];
89 [self->nonSelectedBgColor release];
90 [self->leftCornerIcon release];
91 [self->rightCornerIcon release];
92 [self->leftTabIcon release];
93 [self->selectedTabIcon release];
94 [self->tabIcon release];
95 [self->width release];
96 [self->height release];
97 [self->activeBgColor release];
98 [self->inactiveBgColor release];
99 [self->fontColor release];
100 [self->fontSize release];
101 [self->fontFace release];
102 [self->template release];
108 - (id)saveNestedStateInContext:(WOContext *)_ctx {
111 - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx {
112 if (_state == nil) return;
115 - (NSArray *)filterKeys:(NSArray *)_keys inContext:(WOContext *)_ctx {
120 if (self->disabledTabKeys == nil || _keys == nil)
123 rkeys = [self->disabledTabKeys valueInComponent:[_ctx component]];
124 if (rkeys == nil || (count = [rkeys count]) == 0)
127 ma = [[_keys mutableCopy] autorelease];
128 for (i = 0; i < count; i++) {
131 // TODO: not particulary efficient, but should be OK ... */
132 for (j = 0, jc = [ma count]; j < jc; j++) {
135 info = [ma objectAtIndex:j];
136 if (![info->key isEqualToString:[rkeys objectAtIndex:i]])
139 [ma removeObjectAtIndex:j];
146 - (NSArray *)collectKeysInContext:(WOContext *)_ctx {
149 /* collect mode, collects all keys */
150 [_ctx setObject:WETabView_COLLECT forKey:WETabView_HEAD];
151 [self->template appendToResponse:nil inContext:_ctx];
152 [_ctx removeObjectForKey:WETabView_HEAD];
154 keys = [_ctx objectForKey:WETabView_KEYS];
157 keys = [self filterKeys:keys inContext:_ctx];
164 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
166 NSString *activeTabKey;
168 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
170 nestedState = [self saveNestedStateInContext:_ctx];
171 [_ctx appendElementIDComponent:@"b"];
172 [_ctx appendElementIDComponent:activeTabKey];
174 [_ctx setObject:activeTabKey forKey:WETabView_BODY];
177 [[_ctx component] debugWithFormat:@"WETabView: body takes values, eid='%@'",
181 [self->template takeValuesFromRequest:_req inContext:_ctx];
183 [_ctx removeObjectForKey:WETabView_BODY];
184 [_ctx deleteLastElementIDComponent]; // activeKey
185 [_ctx deleteLastElementIDComponent]; /* 'b' */
186 [self restoreNestedState:nestedState inContext:_ctx];
189 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
194 if ((key = [_ctx currentElementID]) == nil)
198 nestedState = [self saveNestedStateInContext:_ctx];
200 if ([key isEqualToString:@"h"]) {
204 [_ctx consumeElementID];
205 [_ctx appendElementIDComponent:@"h"];
207 if ((urlKey = [_ctx currentElementID]) == nil) {
209 debugWithFormat:@"missing active head tab key !"];
212 //NSLog(@"clicked: %@", urlKey);
213 [_ctx consumeElementID];
214 [_ctx appendElementIDComponent:urlKey];
218 [_ctx setObject:self->selection forKey:WETabView_HEAD];
219 result = [self->template invokeActionForRequest:_req inContext:_ctx];
220 [_ctx removeObjectForKey:WETabView_HEAD];
224 [_ctx deleteLastElementIDComponent]; // active key
226 [_ctx deleteLastElementIDComponent]; // 'h'
228 else if ([key isEqualToString:@"b"]) {
230 NSString *activeTabKey, *urlKey;
232 [_ctx consumeElementID];
233 [_ctx appendElementIDComponent:@"b"];
235 if ((urlKey = [_ctx currentElementID]) == nil) {
237 debugWithFormat:@"missing active body tab key !"];
240 //NSLog(@"clicked: %@", urlKey);
241 [_ctx consumeElementID];
242 [_ctx appendElementIDComponent:urlKey];
245 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
246 [_ctx setObject:activeTabKey forKey:WETabView_BODY];
248 result = [self->template invokeActionForRequest:_req inContext:_ctx];
250 [_ctx removeObjectForKey:WETabView_BODY];
253 [_ctx deleteLastElementIDComponent]; // active key
254 [_ctx deleteLastElementIDComponent]; // 'b'
258 debugWithFormat:@"unknown tab container key '%@'", key];
261 [self restoreNestedState:nestedState inContext:_ctx];
265 - (NSString *)_tabViewCountInContext:(WOContext *)_ctx {
267 count = [[_ctx valueForKey:@"WETabViewScriptDone"] intValue];
268 return [NSString stringWithFormat:@"%d",count];
271 - (NSString *)scriptHref:(WETabItemInfo *)_info
272 inContext:(WOContext *)_ctx
274 keys:(NSArray *)_keys
276 NSMutableString *result = [NSMutableString string];
283 activeKey = [self->selection stringValueInComponent:[_ctx component]];
284 [result appendString:@"JavaScript:showTab("];
285 [result appendString:_info->key];
286 [result appendString:@"Tab);"];
288 [result appendString:@"swapCorners("];
290 ? @"tabCorner%@,tabCornerLeft%@);"
291 : @"tabCornerLeft%@,tabCorner%@);";
292 elID = [self _tabViewCountInContext:_ctx];
293 [result appendString:[NSString stringWithFormat:tstring,elID,elID]];
295 for (i=0, cnt = [_keys count]; i < cnt; i++) {
296 tmp = [_keys objectAtIndex:i];
298 if ((tmp->isScript || [tmp->key isEqualToString:activeKey])
299 && ![tmp->key isEqualToString:_info->key]) {
300 [result appendString:@"hideTab("];
301 [result appendString:tmp->key];
302 [result appendString:@"Tab);"];
308 - (void)appendLink:(WETabItemInfo *)_info
309 toResponse:(WOResponse *)_response
310 inContext:(WOContext *)_ctx
311 isActive:(BOOL)_isActive isLeft:(BOOL)_isLeft
312 doScript:(BOOL)_doScript keys:(NSArray *)_keys
316 NSString *headUri = nil;
317 NSString *imgUri = nil;
318 NSString *label = nil;
319 NSString *bgcolor = nil;
322 NSString *scriptHref = nil;
325 doImages = ![[[_ctx request] clientCapabilities] isTextModeBrowser];
327 comp = [_ctx component];
328 headUri = _info->uri;
330 if (_info->asBackground == 0)
331 doBgIcon = [self->asBackground boolValueInComponent:comp];
333 doBgIcon = (_info->asBackground == 1) ? YES : NO;
335 if ((label = _info->label) == nil)
340 NSString *imgName = nil;
343 imgName = _info->selIcon; // selectedTabIcon
345 imgName = [self->selectedTabIcon stringValueInComponent:comp];
348 imgName = _info->leftIcon; // leftTabIcon
350 imgName = [self->leftTabIcon stringValueInComponent:comp];
353 imgName = _info->tabIcon; // tabIcon
355 imgName = [self->tabIcon stringValueInComponent:comp];
358 if (imgName == nil) {
359 imgName = _info->icon;
362 imgUri = WEUriOfResource(imgName, _ctx);
364 if ([imgUri length] < 1)
369 bgcolor = (_info->activeBg)
371 : [self->activeBgColor stringValueInComponent:comp];
374 bgcolor = (_info->inactiveBg)
376 : [self->inactiveBgColor stringValueInComponent:comp];
381 : [self->width stringValueInComponent:comp];
384 : [self->height stringValueInComponent:comp];
386 [_response appendContentString:@"<td align='center' valign='middle'"];
389 [_response appendContentString:@" width='"];
390 [_response appendContentHTMLAttributeValue:w];
391 [_response appendContentCharacter:'\''];
394 [_response appendContentString:@" height='"];
395 [_response appendContentHTMLAttributeValue:h];
396 [_response appendContentCharacter:'\''];
398 if (bgcolor != nil) {
399 [_response appendContentString:@" bgcolor='"];
400 [_response appendContentHTMLAttributeValue:bgcolor];
401 [_response appendContentCharacter:'\''];
403 if (doBgIcon && doImages) {
404 WEClientCapabilities *ccaps;
406 ccaps = [[_ctx request] clientCapabilities];
408 [_response appendContentString:@" background='"];
409 [_response appendContentHTMLAttributeValue:imgUri];
410 [_response appendContentCharacter:'\''];
412 // click on td background
413 if ([ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]) {
414 [_response appendContentString:@" onclick=\"window.location.href='"];
415 [_response appendContentHTMLAttributeValue:headUri];
416 [_response appendContentString:@"'\""];
420 [_response appendContentCharacter:'>'];
422 if (!doImages) [_response appendContentString:@"["];
424 [_response appendContentString:@"<a style=\"text-decoration:none;\" href=\""];
426 if (_doScript && doImages && (_info->isScript || _isActive)) {
428 [self scriptHref:_info inContext:_ctx isLeft:_isLeft keys:_keys];
429 [_response appendContentHTMLAttributeValue:scriptHref];
432 [_response appendContentHTMLAttributeValue:headUri];
435 [_response appendContentString:@"\" "];
436 [_response appendContentString:
437 [NSString stringWithFormat:@"name='%@TabLink'", _info->key]];
438 [_response appendContentString:@">"];
440 if (!doImages && _isActive)
441 [_response appendContentString:@"<b>"];
443 if (doImages && !doBgIcon) {
444 [_response appendContentString:@"<img border='0' src='"];
445 [_response appendContentString:imgUri];
446 [_response appendContentString:@"' name='"];
447 [_response appendContentString:_info->key];
448 [_response appendContentString:@"TabImg' alt='"];
449 [_response appendContentHTMLAttributeValue:label];
450 [_response appendContentString:@"' title='"];
451 [_response appendContentHTMLAttributeValue:label];
452 [_response appendContentString:@"'"];
453 if (_ctx->wcFlags.xmlStyleEmptyElements)
454 [_response appendContentString:@" />"];
456 [_response appendContentString:@">"];
459 NSString *fc = [self->fontColor stringValueInComponent:comp];
460 NSString *fs = [self->fontSize stringValueInComponent:comp];
461 NSString *ff = [self->fontFace stringValueInComponent:comp];
464 hasFont = (fc || fs || ff) ? YES : NO;
466 if ([label length] < 1)
468 [_response appendContentString:@"<nobr>"];
469 if (hasFont) WEAppendFont(_response, fc, ff, fs); // <font>
471 if (_isActive) [_response appendContentString:@"<b>"];
472 [_response appendContentHTMLString:label];
473 if (_isActive) [_response appendContentString:@"</b>"];
475 if (hasFont) [_response appendContentString:@"</font>"]; // </font>
476 [_response appendContentString:@"</nobr>"];
479 if (!doImages && _isActive)
480 [_response appendContentString:@"</b>"];
482 [_response appendContentString:@"</a>"];
483 if (!doImages) [_response appendContentString:@"]"];
486 [_response appendContentString:@"</td>"];
488 if (_doScript && doImages && (_info->isScript || _isActive)) {
490 NSString *s; // selected tab icon
491 NSString *u; // unselected tab icon
495 s = _info->selIcon; // selectedTabIcon
496 u = (_isLeft) ? _info->leftIcon : _info->tabIcon;
498 s = WEUriOfResource(s, _ctx);
499 u = WEUriOfResource(u, _ctx);
501 s = ([s length] < 1) ? imgUri : s;
502 u = ([u length] < 1) ? imgUri : u;
505 out = [NSString alloc];
506 out = [out initWithFormat:
507 @"<script language=\"JavaScript\">\n"
509 @"var %@Tab = new Array();\n"
510 @"%@Tab[\"link\"] = %@TabLink;\n"
511 @"%@Tab[\"href1\"] = \"%@\";\n"
512 @"%@Tab[\"href2\"] = \"%@\";\n"
513 @"%@Tab[\"Img\"] = window.document.%@TabImg;\n"
514 @"%@Tab[\"Ar\"] = new Array();\n"
515 @"%@Tab[\"Ar\"][0] = new Image();\n"
516 @"%@Tab[\"Ar\"][0].src = \"%@\";\n"
517 @"%@Tab[\"Ar\"][1] = new Image();\n"
518 @"%@Tab[\"Ar\"][1].src = \"%@\";\n"
520 k, k, k, k, scriptHref, k, headUri,
525 [_response appendContentString:out];
528 # define _appendStr_(_str_) [_response appendContentString:_str_]
529 _appendStr_(@"<script language=\"JavaScript\">\n<!--\nvar ");
530 _appendStr_(k); _appendStr_(@"Tab = new Array();\n");
532 _appendStr_(k); _appendStr_(@"Tab[\"link\"] = ");
533 _appendStr_(k); _appendStr_(@"TabLink;\n"); // linkName
535 _appendStr_(k); _appendStr_(@"Tab[\"href1\"] = \"");
536 _appendStr_(scriptHref); _appendStr_(@"\";\n"); // scriptHref
538 _appendStr_(k); _appendStr_(@"Tab[\"href2\"] = \"");
539 _appendStr_(_info->uri); _appendStr_(@"\";\n"); // actionHref
541 _appendStr_(k); _appendStr_(@"Tab[\"Img\"] = window.document.");
542 _appendStr_(k); _appendStr_(@"TabImg;\n");
543 _appendStr_(k); _appendStr_(@"Tab[\"Ar\"] = new Array();\n");
544 _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][0] = new Image();\n");
545 _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][0].src = \"");
546 _appendStr_(u); _appendStr_(@"\";\n"); // unselected img
547 _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][1] = new Image();\n");
548 _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][1].src = \"");
549 _appendStr_(s); _appendStr_(@"\";\n"); // selected img
550 _appendStr_(@"//-->\n</script>");
556 - (void)appendSubmitButton:(WETabItemInfo *)_info
557 toResponse:(WOResponse *)_response
558 inContext:(WOContext *)_ctx
559 isActive:(BOOL)_isActive isLeft:(BOOL)_left
560 doScript:(BOOL)_doScript keys:(NSArray *)_keys
562 [self appendLink:_info
565 isActive:_isActive isLeft:_left
566 doScript:_doScript keys:_keys];
569 - (void)_appendTabViewJSScriptToResponse:(WOResponse *)_response
570 inContext:(WOContext *)_ctx
572 [_response appendContentString:
573 @"<script language=\"JavaScript\">\n<!--\n\n"
574 @"function showTab(obj) {\n"
576 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
577 @" if (obj['Div']==null) {"
578 @" alert('missing div key in ' + obj); return; }\n"
579 @" if (obj['Div'].style==null) {"
580 @" alert('missing style key in div ' + obj['Div']);return; }\n"
582 @" obj['Div'].style.display = \"\";\n"
583 @" obj['Img'].src = obj[\"Ar\"][1].src;\n"
584 @" obj['link'].href = obj[\"href2\"];\n"
586 @"function hideTab(obj) {\n"
588 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
589 @" if (obj['Div']==null) {"
590 @" alert('missing div key in ' + obj); return; }\n"
591 @" if (obj['Div'].style==null) {"
592 @" alert('missing style key in div ' + obj['Div']);return; }\n"
594 @" obj['Div'].style.display = \"none\";\n"
595 @" obj['Img'].src = obj[\"Ar\"][0].src;\n"
596 @" obj['link'].href = obj[\"href1\"];\n"
598 @"function swapCorners(obj1,obj2) {\n"
599 @" if (obj1==null) { alert('missing corner 1'); return; }\n"
600 @" if (obj2==null) { alert('missing corner 2'); return; }\n"
601 @" obj1.style.display = \"none\";\n"
602 @" obj2.style.display = \"\";\n"
604 @"//-->\n</script>"];
607 - (void)_appendHeaderRowToResponse:(WOResponse *)_response
608 inContext:(WOContext *)_ctx
609 keys:(NSArray *)keys activeKey:(NSString *)activeKey
610 doScript:(BOOL)doScript
615 doForm = NO; /* generate form controls ? */
617 [_response appendContentString:@"<tr><td colspan='2'>"];
618 [_response appendContentString:
619 @"<table border='0' cellpadding='0' cellspacing='0'><tr>"];
621 for (i = 0, count = [keys count]; i < count; i++) {
626 info = [keys objectAtIndex:i];
628 isActive = [key isEqualToString:activeKey];
630 [_ctx appendElementIDComponent:key];
633 /* tab is inside of a FORM, so produce submit buttons */
634 [self appendSubmitButton:info
638 isLeft:(i == 0) ? YES : NO
643 /* tab is not in a FORM, generate hyperlinks for tab */
644 [self appendLink:info
648 isLeft:(i == 0) ? YES : NO
653 [_ctx deleteLastElementIDComponent];
655 // [_response appendContentString:@"<td></td>"];
656 [_response appendContentString:@"</tr></table>"];
657 [_response appendContentString:@"</td></tr>"];
660 - (void)_appendHeaderFootRowToResponse:(WOResponse *)_response
661 inContext:(WOContext *)_ctx
662 bgcolor:(NSString *)bgcolor
663 doScript:(BOOL)doScript
664 isLeftActive:(BOOL)isLeftActive
666 [_response appendContentString:@" <tr"];
668 [_response appendContentString:@" bgcolor=\""];
669 [_response appendContentHTMLAttributeValue:bgcolor];
670 [_response appendContentString:@"\""];
672 [_response appendContentString:@">"];
675 [_response appendContentString:@" <td align=\"left\" width=\"10\">"];
678 [_response appendContentString:@"<div id=\"tabCorner"];
679 [_response appendContentString:[self _tabViewCountInContext:_ctx]];
680 [_response appendContentString:@"\" "];
681 [_response appendContentString:@"style=\"display: "];
682 [_response appendContentString:(isLeftActive) ? @"" : @"none"];
683 [_response appendContentString:@";\">"];
684 [_response appendContentString:@" "];
685 [_response appendContentString:@"</div>"];
687 else if (isLeftActive)
688 [_response appendContentString:@" "];
691 [_response appendContentString:@"<div id=\"tabCornerLeft"];
692 [_response appendContentString:[self _tabViewCountInContext:_ctx]];
693 [_response appendContentString:@"\" "];
694 [_response appendContentString:@"style=\"display: "];
695 [_response appendContentString:(!isLeftActive) ? @"visible" : @"none"];
696 [_response appendContentString:@";\">"];
699 if (!isLeftActive || doScript) {
702 uri = [self->leftCornerIcon stringValueInComponent:[_ctx component]];
703 if ((uri = WEUriOfResource(uri, _ctx))) {
704 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
705 [_response appendContentString:uri];
706 [_response appendContentString:@"\" />"];
709 [_response appendContentString:@" "];
712 [_response appendContentString:@"</div>"];
714 [_response appendContentString:@"</td>"];
717 [_response appendContentString:@" <td align=\"right\">"];
721 uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]];
722 if ((uri = WEUriOfResource(uri, _ctx))) {
723 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
724 [_response appendContentString:uri];
725 if (_ctx->wcFlags.xmlStyleEmptyElements)
726 [_response appendContentString:@"\" />"];
728 [_response appendContentString:@"\">"];
731 [_response appendContentString:@" "];
733 [_response appendContentString:@"</td>"];
735 [_response appendContentString:@" </tr>"];
738 - (void)_appendBodyRowToResponse:(WOResponse *)_response
739 inContext:(WOContext *)_ctx
740 bgcolor:(NSString *)bgcolor
741 activeKey:(NSString *)activeKey
743 WEClientCapabilities *ccaps;
746 ccaps = [[_ctx request] clientCapabilities];
748 /* put additional padding table into content ??? */
749 indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser];
751 [_response appendContentString:@"<tr><td colspan='2'"];
754 [_response appendContentString:@" bgcolor=\""];
755 [_response appendContentHTMLAttributeValue:bgcolor];
756 [_response appendContentString:@"\""];
758 [_response appendContentString:@">"];
760 if (indentContent) { // TODO: we can now replace that with CSS padding?
761 /* start padding table */
762 [_response appendContentString:
763 @"<table border='0' width='100%'"
764 @" cellpadding='10' cellspacing='0'>"];
765 [_response appendContentString:@"<tr><td>"];
768 [_ctx appendElementIDComponent:@"b"];
769 [_ctx appendElementIDComponent:activeKey];
771 /* generate currently active body */
773 [_ctx setObject:activeKey forKey:WETabView_BODY];
774 [self->template appendToResponse:_response inContext:_ctx];
775 [_ctx removeObjectForKey:WETabView_BODY];
778 [_ctx deleteLastElementIDComponent]; // activeKey
779 [_ctx deleteLastElementIDComponent]; // 'b'
782 /* close padding table */
783 [_response appendContentString:@"</td></tr></table>"];
785 [_response appendContentString:@"</td></tr>"];
788 - (BOOL)isLeftActiveInKeys:(NSArray *)keys activeKey:(NSString *)activeKey{
794 for (i = 0, count = [keys count]; i < count; i++) {
797 info = [keys objectAtIndex:i];
799 if ((i == 0) && [info->key isEqualToString:activeKey])
806 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
808 generates a table with three rows:
809 - a row with the tabs
810 - a row for round edges
811 - a row for the content
820 int tabViewCount; /* used for image id's and writing script once */
822 doScript = NO; /* perform tab-clicks on browser (use javascript) */
823 tabViewCount = [[_ctx valueForKey:@"WETabViewScriptDone"] intValue];
824 cmp = [_ctx component];
826 /* check for browser */
828 WEClientCapabilities *ccaps;
829 //BOOL isJavaScriptBrowser;
831 ccaps = [[_ctx request] clientCapabilities];
832 doScript = [ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser];
833 if ([_ctx hasSession]) {
837 doScript = (doScript &&
838 [[sn valueForKey:@"isJavaScriptEnabled"] boolValue]);
842 /* disable javascript */
847 nestedState = [self saveNestedStateInContext:_ctx];
851 activeKey = [self->selection stringValueInComponent:cmp];
853 bgcolor = [self->bgColor stringValueInComponent:cmp];
854 bgcolor = [bgcolor stringValue];
856 [_ctx appendElementIDComponent:@"h"];
858 /* collect & process keys (= available tabs) */
860 keys = [self collectKeysInContext:_ctx];
862 if (![[keys valueForKey:@"key"] containsObject:activeKey])
863 /* selection is not available in keys */
866 if ((activeKey == nil) && ([keys count] > 0)) {
867 /* no or invalid selection, use first key */
868 activeKey = [[keys objectAtIndex:0] key];
869 if ([self->selection isValueSettable])
870 [self->selection setValue:activeKey inComponent:[_ctx component]];
874 doScript = [[keys valueForKey:@"isScript"] containsObject:YesNumber];
875 [_ctx setObject:[NSNumber numberWithBool:doScript]
876 forKey:WETabView_SCRIPT];
879 /* start appending */
881 if ((doScript) && (tabViewCount == 0))
882 [self _appendTabViewJSScriptToResponse:_response inContext:_ctx];
884 /* count up for unique tabCorner/tabCornerLeft images */
885 [_ctx takeValue:[NSNumber numberWithInt:(tabViewCount + 1)]
886 forKey:@"WETabViewScriptDone"];
888 // TODO: add CSS class for table
889 [_response appendContentString:
890 @"<table border='0' width='100%'"
891 @" cellpadding='0' cellspacing='0'>"];
893 /* find out whether left is active */
895 isLeftActive = [self isLeftActiveInKeys:keys activeKey:activeKey];
897 /* generate header row */
899 [self _appendHeaderRowToResponse:_response inContext:_ctx
900 keys:keys activeKey:activeKey
903 [_ctx deleteLastElementIDComponent]; // 'h' for head
904 [_ctx removeObjectForKey:WETabView_HEAD];
906 /* header foot row */
908 [self _appendHeaderFootRowToResponse:_response inContext:_ctx
911 isLeftActive:isLeftActive];
915 [self _appendBodyRowToResponse:_response inContext:_ctx
917 activeKey:activeKey];
921 [_response appendContentString:@"</table>"];
922 [_ctx removeObjectForKey:WETabView_ACTIVEKEY];
923 [_ctx removeObjectForKey:WETabView_KEYS];
924 [self restoreNestedState:nestedState inContext:_ctx];