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 <NGObjWeb/WODynamicElement.h>
26 @interface WECollapsibleComponentContent : WODynamicElement
29 WOAssociation *condition;
30 WOAssociation *visibility;
32 WOAssociation *allowScript; /* perform clicks on browser (use javaScript) */
38 @interface WECollapsibleAction : WODynamicElement
41 WOAssociation *openedImageFileName;
42 WOAssociation *closedImageFileName;
43 WOAssociation *framework;
44 WOAssociation *openedLabel;
45 WOAssociation *closedLabel;
46 WOAssociation *submitActionName;
47 WOAssociation *action; // if submit button, use submitActionName instead
48 WOAssociation *fragmentIdentifier;
49 WOAssociation *isClicked;
55 #include "WEContextConditional.h"
56 #include <NGObjWeb/WEClientCapabilities.h>
59 static NSString *WECollapsible_TitleMode = @"WECollapsible_TitleMode";
60 static NSString *WECollapsible_ContentMode = @"WECollapsible_ContentMode";
61 static NSString *WECollapsible_IsCollapsed = @"WECollapsible_IsCollapsed";
62 static NSString *WECollapsible_ScriptId = @"WECollapsible_ScriptId";
63 static NSString *WECollapsible_HasScript = @"WECollapsible_HasScript";
64 static NSString *Yes = @"YES";
65 static NSString *No = @"NO";
67 @implementation WECollapsibleComponentContent
69 - (id)initWithName:(NSString *)_name
70 associations:(NSDictionary *)_config
71 template:(WOElement *)_tmp
73 if ((self = [super initWithName:_name associations:_config template:_tmp])) {
74 self->condition = WOExtGetProperty(_config, @"condition");
75 self->visibility = WOExtGetProperty(_config, @"visibility");
76 self->allowScript = WOExtGetProperty(_config, @"allowScript");
78 if (self->visibility == nil)
79 NSLog(@"WARNING: WECollapsibleComponent 'visibility' not set");
81 if (self->visibility && ![self->visibility isValueSettable])
82 NSLog(@"WARNING: WECollapsibleComponent 'visibility' is not settable");
84 self->template = [_tmp retain];
90 [self->condition release];
91 [self->visibility release];
92 [self->allowScript release];
93 [self->template release];
99 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
102 isCollapsed = ![self->visibility boolValueInComponent:[_ctx component]];
106 [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
107 [self->template takeValuesFromRequest:_req inContext:_ctx];
108 [_ctx removeObjectForKey:WECollapsible_ContentMode];
112 [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
114 [self->template takeValuesFromRequest:_req inContext:_ctx];
116 [_ctx removeObjectForKey:WECollapsible_TitleMode];
119 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
123 isCollapsed = ![self->visibility boolValueInComponent:[_ctx component]];
126 [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
127 [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
128 [_ctx setObject:(isCollapsed) ? Yes : No forKey:WECollapsible_IsCollapsed];
130 result = [self->template invokeActionForRequest:_request inContext:_ctx];
131 isCollapsed = [[_ctx objectForKey:WECollapsible_IsCollapsed] boolValue];
133 if ([self->visibility isValueSettable])
134 [self->visibility setBoolValue:!isCollapsed inComponent:[_ctx component]];
136 [_ctx removeObjectForKey:WECollapsible_IsCollapsed];
137 [_ctx removeObjectForKey:WECollapsible_ContentMode];
138 [_ctx removeObjectForKey:WECollapsible_TitleMode];
143 - (void)appendToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
149 if ([_ctx isRenderingDisabled]) {
150 [self->template appendToResponse:_resp inContext:_ctx];
154 comp = [_ctx component];
155 doScript = [self->allowScript boolValueInComponent:comp];
156 scriptId = [[[_ctx elementID] componentsSeparatedByString:@"."]
157 componentsJoinedByString:@"_"];
160 WEClientCapabilities *ccaps;
162 ccaps = [[_ctx request] clientCapabilities];
163 doScript = [ccaps isInternetExplorer];
167 [_ctx setObject:scriptId forKey:WECollapsible_ScriptId];
169 if ([self->visibility valueInComponent:comp] == nil) {
170 isCollapsed = ![self->condition boolValueInComponent:comp];
171 if ([self->visibility isValueSettable])
172 [self->visibility setBoolValue:!isCollapsed inComponent:comp];
175 isCollapsed = ![self->visibility boolValueInComponent:comp];
178 [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
179 [_ctx setObject:(isCollapsed) ? Yes : No forKey:WECollapsible_IsCollapsed];
181 [self->template appendToResponse:_resp inContext:_ctx];
183 [_ctx removeObjectForKey:WECollapsible_IsCollapsed];
184 [_ctx removeObjectForKey:WECollapsible_TitleMode];
187 if (!isCollapsed || doScript) {
188 [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
190 [_resp appendContentString:@"<div class=\"collapsible\" "];
191 [_resp appendContentString:@"id=\"collapsible"];
192 [_resp appendContentString:scriptId];
193 [_resp appendContentString:@"\" style=\"display:"];
194 [_resp appendContentString:(isCollapsed) ? @"none" : @"block"];
195 [_resp appendContentString:@";\">"];
198 [self->template appendToResponse:_resp inContext:_ctx];
201 [_resp appendContentString:@"</div>"];
202 [_ctx removeObjectForKey:WECollapsible_ContentMode];
204 [_ctx removeObjectForKey:WECollapsible_ScriptId];
206 @end /* WECollapsibleComponentContent */
208 @implementation WECollapsibleAction
210 - (id)initWithName:(NSString *)_name
211 associations:(NSDictionary *)_config
212 template:(WOElement *)_temp
214 if ((self = [super initWithName:_name associations:_config template:_temp])) {
215 self->openedImageFileName = WOExtGetProperty(_config, @"openedImageFileName");
216 self->closedImageFileName = WOExtGetProperty(_config, @"closedImageFileName");
217 self->framework = WOExtGetProperty(_config, @"framework");
218 self->openedLabel = WOExtGetProperty(_config, @"openedLabel");
219 self->closedLabel = WOExtGetProperty(_config, @"closedLabel");
220 self->submitActionName = WOExtGetProperty(_config, @"submitActionName");
221 self->action = WOExtGetProperty(_config, @"action");
222 self->fragmentIdentifier = WOExtGetProperty(_config, @"fragmentIdentifier");
223 self->isClicked = WOExtGetProperty(_config, @"isClicked");
225 self->template = [_temp retain];
231 [self->openedImageFileName release];
232 [self->closedImageFileName release];
233 [self->framework release];
234 [self->openedLabel release];
235 [self->closedLabel release];
236 [self->submitActionName release];
237 [self->action release];
238 [self->fragmentIdentifier release];
239 [self->isClicked release];
240 [self->template release];
246 - (void)takeValuesFromRequest:(WORequest *)_request
247 inContext:(WOContext *)_ctx
251 eid = [_ctx elementID];
253 if ([_request formValueForKey:[eid stringByAppendingString:@".c.x"]]) {
254 [_ctx addActiveFormElement:self];
255 [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".c"]];
257 else if ([_request formValueForKey:[eid stringByAppendingString:@".e.x"]]) {
258 [_ctx addActiveFormElement:self];
259 [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".e"]];
263 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
267 state = [[_ctx currentElementID] stringValue];
269 doForm = ([_ctx isInForm] && self->submitActionName &&
270 ([self->submitActionName valueInComponent:[_ctx component]]));
272 [_ctx consumeElementID]; // consume state-id
274 if ([state isEqualToString:@"e"]) {
275 [_ctx setObject:[NSNumber numberWithBool:YES]
276 forKey:WECollapsible_IsCollapsed];
279 [self->submitActionName valueInComponent:[_ctx component]];
280 else if ([self->action valueInComponent:[_ctx component]] != nil)
281 [self->action valueInComponent:[_ctx component]];
283 else if ([state isEqualToString:@"c"]) {
284 [_ctx setObject:[NSNumber numberWithBool:NO]
285 forKey:WECollapsible_IsCollapsed];
288 [self->submitActionName valueInComponent:[_ctx component]];
289 else if ([self->action valueInComponent:[_ctx component]] != nil)
290 [self->action valueInComponent:[_ctx component]];
292 if ([self->isClicked isValueSettable])
293 [self->isClicked setBoolValue:YES inComponent:[_ctx component]];
298 - (void)_appendScriptWithID:(NSString *)scriptId
299 label:(NSString *)label imageURL:(NSString *)img
300 toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx
303 NSString *openedImg = nil;
304 NSString *closedImg = nil;
305 NSString *openLabel = nil;
306 NSString *closeLabel = nil;
308 comp = [_ctx component];
309 closedImg = [self->closedImageFileName stringValueInComponent:comp];
310 openedImg = [self->openedImageFileName stringValueInComponent:comp];
311 openLabel = [self->openedLabel stringValueInComponent:comp];
312 closeLabel = [self->closedLabel stringValueInComponent:comp];
314 closedImg = WEUriOfResource(closedImg, _ctx);
315 openedImg = WEUriOfResource(openedImg, _ctx);
317 if (![_ctx objectForKey:WECollapsible_HasScript]) {
318 [_resp appendContentString:
319 @"\n<script language=\"JavaScript\">\n"
321 @"function toggleColl(el, img1, img2, alt1, alt2)\n"
323 @" whichEl = eval(\"collapsible\" + el);\n"
324 @" whichIm = event.srcElement;\n"
325 @" if (whichEl.style.display == \"none\") {\n"
326 @" whichEl.style.display = \"block\";\n"
327 @" whichIm.src = img1;\n"
328 @" whichIm.alt = alt1;\n"
331 @" whichEl.style.display = \"none\";\n"
332 @" whichIm.src = img2;\n"
333 @" whichIm.alt = alt2;\n"
338 [_ctx setObject:Yes forKey:WECollapsible_HasScript];
341 [_resp appendContentString:@"<a href=\"#\" onclick=\"toggleColl('"];
342 [_resp appendContentString:scriptId];
343 [_resp appendContentString:@"','"];
344 [_resp appendContentHTMLString:openedImg];
345 [_resp appendContentString:@"','"];
346 [_resp appendContentHTMLString:closedImg];
347 [_resp appendContentString:@"','"];
348 [_resp appendContentHTMLString:openLabel];
349 [_resp appendContentString:@"','"];
350 [_resp appendContentHTMLString:closeLabel];
351 [_resp appendContentString:@"'); return false\"><img "];
353 [_resp appendContentString:@"alt=\""];
354 [_resp appendContentString:label];
355 [_resp appendContentString:@"\" title=\""];
356 [_resp appendContentString:label];
357 [_resp appendContentString:@"\" "];
359 [_resp appendContentString:@"border=\"0\" name=\"imEx\" src=\""];
360 [_resp appendContentString:img];
361 [_resp appendContentString:@"\" /></a>"];
364 - (void)_appendLinkWithFragmentID:(NSString *)fragId
365 label:(NSString *)label imageURL:(NSString *)img
366 isCollapsed:(BOOL)isCollapsed
367 toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx
369 [_resp appendContentString:@"<a href=\""];
370 [_resp appendContentString:[_ctx componentActionURL]];
371 if ([fragId isNotEmpty]) {
372 [_resp appendContentString:@"#"];
373 [_resp appendContentString:fragId];
375 [_resp appendContentString:@"\">"];
378 [_resp appendContentString:@"<img border=\"0\" src=\""];
379 [_resp appendContentString:img];
380 [_resp appendContentString:@"\""];
382 [_resp appendContentString:@" alt=\""];
383 [_resp appendContentString:label];
384 [_resp appendContentString:@"\" title=\""];
385 [_resp appendContentString:label];
386 [_resp appendContentString:@"\""];
388 [_resp appendContentString:@" />"];
391 [_resp appendContentString:(isCollapsed) ? @"[+]" : @"[-]"];
393 [_resp appendContentString:@"</a>"];
396 - (void)appendToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
397 /* TODO: split up this huge method */
403 NSString *label = nil;
404 NSString *fragId = nil;
405 NSString *scriptId = nil;
407 if ([_ctx isRenderingDisabled]) {
408 [self->template appendToResponse:_resp inContext:_ctx];
412 comp = [_ctx component];
413 fragId = [self->fragmentIdentifier stringValueInComponent:comp];
415 isCollapsed = [[_ctx objectForKey:WECollapsible_IsCollapsed] boolValue];
416 scriptId = [_ctx objectForKey:WECollapsible_ScriptId];
417 doScript = [scriptId isNotEmpty] ? YES : NO;
420 ? [self->closedImageFileName stringValueInComponent:comp]
421 : [self->openedImageFileName stringValueInComponent:comp];
423 label = (isCollapsed)
424 ? [self->closedLabel stringValueInComponent:comp]
425 : [self->openedLabel stringValueInComponent:comp];
427 img = WEUriOfResource(img, _ctx);
431 [_resp appendContentString:@" "];
434 doForm = ([_ctx isInForm] && self->submitActionName && img);
436 [_ctx appendElementIDComponent:(isCollapsed) ? @"c" : @"e"];
438 [self _appendScriptWithID:scriptId label:label imageURL:img
439 toResponse:_resp inContext:_ctx];
442 [_resp appendContentString:@"<input type=\"image\" border=\"0\" name=\""];
443 [_resp appendContentString:[_ctx elementID]];
444 [_resp appendContentString:@"\" src=\""];
445 [_resp appendContentString:img];
446 [_resp appendContentString:@"\" />"];
449 [self _appendLinkWithFragmentID:fragId label:label imageURL:img
450 isCollapsed:isCollapsed
451 toResponse:_resp inContext:_ctx];
455 WEClientCapabilities *ccaps;
457 ccaps = [[_ctx request] clientCapabilities];
459 [_resp appendContentString:@"<a name=\""];
460 [_resp appendContentString:fragId];
461 [_resp appendContentString:@"\"> </a>"];
462 if ([self->isClicked boolValueInComponent:comp] &&
463 ([ccaps isInternetExplorer] || [ccaps isMozilla] || [ccaps isNetscape6])) {
464 if ([self->isClicked isValueSettable])
465 [self->isClicked setBoolValue:NO inComponent:comp];
467 [_resp appendContentString:
468 [NSString stringWithFormat:
469 @"\n<script language=\"JavaScript\">\n"
471 @" window.location.hash=\"#%@\";\n"
478 [_resp appendContentString:@" "];
480 if (label && !doScript) {
482 [_resp appendContentString:@"<a href=\""];
483 [_resp appendContentString:[_ctx componentActionURL]];
485 [_resp appendContentString:@"#"];
486 [_resp appendContentString:fragId];
488 [_resp appendContentString:@"\">"];
491 [_resp appendContentString:label];
494 [_resp appendContentString:@"</a>"];
496 [_ctx deleteLastElementIDComponent];
499 @end /* WECollapsibleAction */
501 @interface WECollapsibleTitleMode: WEContextConditional
504 @implementation WECollapsibleTitleMode
506 - (NSString *)_contextKey {
507 return WECollapsible_TitleMode;
510 @end /* WECollapsibleTitleMode */
512 @interface WECollapsibleContentMode: WEContextConditional
515 @implementation WECollapsibleContentMode
517 - (NSString *)_contextKey {
518 return WECollapsible_ContentMode;
521 @end /* WECollapsibleContentMode */