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 comp = [_ctx component];
150 doScript = [self->allowScript boolValueInComponent:comp];
151 scriptId = [[[_ctx elementID] componentsSeparatedByString:@"."]
152 componentsJoinedByString:@"_"];
155 WEClientCapabilities *ccaps;
157 ccaps = [[_ctx request] clientCapabilities];
158 doScript = [ccaps isInternetExplorer];
162 [_ctx setObject:scriptId forKey:WECollapsible_ScriptId];
164 if ([self->visibility valueInComponent:comp] == nil) {
165 isCollapsed = ![self->condition boolValueInComponent:comp];
166 if ([self->visibility isValueSettable])
167 [self->visibility setBoolValue:!isCollapsed inComponent:comp];
170 isCollapsed = ![self->visibility boolValueInComponent:comp];
173 [_ctx setObject:Yes forKey:WECollapsible_TitleMode];
174 [_ctx setObject:(isCollapsed) ? Yes : No forKey:WECollapsible_IsCollapsed];
176 [self->template appendToResponse:_resp inContext:_ctx];
178 [_ctx removeObjectForKey:WECollapsible_IsCollapsed];
179 [_ctx removeObjectForKey:WECollapsible_TitleMode];
182 if (!isCollapsed || doScript) {
183 [_ctx setObject:Yes forKey:WECollapsible_ContentMode];
185 [_resp appendContentString:@"<div class=\"collapsible\" "];
186 [_resp appendContentString:@"id=\"collapsible"];
187 [_resp appendContentString:scriptId];
188 [_resp appendContentString:@"\" style=\"display:"];
189 [_resp appendContentString:(isCollapsed) ? @"none" : @"block"];
190 [_resp appendContentString:@";\">"];
193 [self->template appendToResponse:_resp inContext:_ctx];
196 [_resp appendContentString:@"</div>"];
197 [_ctx removeObjectForKey:WECollapsible_ContentMode];
199 [_ctx removeObjectForKey:WECollapsible_ScriptId];
201 @end /* WECollapsibleComponentContent */
203 @implementation WECollapsibleAction
205 - (id)initWithName:(NSString *)_name
206 associations:(NSDictionary *)_config
207 template:(WOElement *)_temp
209 if ((self = [super initWithName:_name associations:_config template:_temp])) {
210 self->openedImageFileName = WOExtGetProperty(_config, @"openedImageFileName");
211 self->closedImageFileName = WOExtGetProperty(_config, @"closedImageFileName");
212 self->framework = WOExtGetProperty(_config, @"framework");
213 self->openedLabel = WOExtGetProperty(_config, @"openedLabel");
214 self->closedLabel = WOExtGetProperty(_config, @"closedLabel");
215 self->submitActionName = WOExtGetProperty(_config, @"submitActionName");
216 self->action = WOExtGetProperty(_config, @"action");
217 self->fragmentIdentifier = WOExtGetProperty(_config, @"fragmentIdentifier");
218 self->isClicked = WOExtGetProperty(_config, @"isClicked");
220 self->template = [_temp retain];
226 [self->openedImageFileName release];
227 [self->closedImageFileName release];
228 [self->framework release];
229 [self->openedLabel release];
230 [self->closedLabel release];
231 [self->submitActionName release];
232 [self->action release];
233 [self->fragmentIdentifier release];
234 [self->isClicked release];
235 [self->template release];
241 - (void)takeValuesFromRequest:(WORequest *)_request
242 inContext:(WOContext *)_ctx
246 eid = [_ctx elementID];
248 if ([_request formValueForKey:[eid stringByAppendingString:@".c.x"]]) {
249 [_ctx addActiveFormElement:self];
250 [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".c"]];
252 else if ([_request formValueForKey:[eid stringByAppendingString:@".e.x"]]) {
253 [_ctx addActiveFormElement:self];
254 [_ctx setRequestSenderID:[[_ctx senderID] stringByAppendingString:@".e"]];
258 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
262 state = [[_ctx currentElementID] stringValue];
264 doForm = ([_ctx isInForm] && self->submitActionName &&
265 ([self->submitActionName valueInComponent:[_ctx component]]));
267 [_ctx consumeElementID]; // consume state-id
269 if ([state isEqualToString:@"e"]) {
270 [_ctx setObject:[NSNumber numberWithBool:YES]
271 forKey:WECollapsible_IsCollapsed];
274 [self->submitActionName valueInComponent:[_ctx component]];
275 else if ([self->action valueInComponent:[_ctx component]] != nil)
276 [self->action valueInComponent:[_ctx component]];
278 else if ([state isEqualToString:@"c"]) {
279 [_ctx setObject:[NSNumber numberWithBool:NO]
280 forKey:WECollapsible_IsCollapsed];
283 [self->submitActionName valueInComponent:[_ctx component]];
284 else if ([self->action valueInComponent:[_ctx component]] != nil)
285 [self->action valueInComponent:[_ctx component]];
287 if ([self->isClicked isValueSettable])
288 [self->isClicked setBoolValue:YES inComponent:[_ctx component]];
293 - (void)_appendScriptWithID:(NSString *)scriptId
294 label:(NSString *)label imageURL:(NSString *)img
295 toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx
298 NSString *openedImg = nil;
299 NSString *closedImg = nil;
300 NSString *openLabel = nil;
301 NSString *closeLabel = nil;
303 comp = [_ctx component];
304 closedImg = [self->closedImageFileName stringValueInComponent:comp];
305 openedImg = [self->openedImageFileName stringValueInComponent:comp];
306 openLabel = [self->openedLabel stringValueInComponent:comp];
307 closeLabel = [self->closedLabel stringValueInComponent:comp];
309 closedImg = WEUriOfResource(closedImg, _ctx);
310 openedImg = WEUriOfResource(openedImg, _ctx);
312 if (![_ctx objectForKey:WECollapsible_HasScript]) {
313 [_resp appendContentString:
314 @"\n<script language=\"JavaScript\">\n"
316 @"function toggleColl(el, img1, img2, alt1, alt2)\n"
318 @" whichEl = eval(\"collapsible\" + el);\n"
319 @" whichIm = event.srcElement;\n"
320 @" if (whichEl.style.display == \"none\") {\n"
321 @" whichEl.style.display = \"block\";\n"
322 @" whichIm.src = img1;\n"
323 @" whichIm.alt = alt1;\n"
326 @" whichEl.style.display = \"none\";\n"
327 @" whichIm.src = img2;\n"
328 @" whichIm.alt = alt2;\n"
333 [_ctx setObject:Yes forKey:WECollapsible_HasScript];
336 [_resp appendContentString:@"<a href=\"#\" onclick=\"toggleColl('"];
337 [_resp appendContentString:scriptId];
338 [_resp appendContentString:@"','"];
339 [_resp appendContentHTMLString:openedImg];
340 [_resp appendContentString:@"','"];
341 [_resp appendContentHTMLString:closedImg];
342 [_resp appendContentString:@"','"];
343 [_resp appendContentHTMLString:openLabel];
344 [_resp appendContentString:@"','"];
345 [_resp appendContentHTMLString:closeLabel];
346 [_resp appendContentString:@"'); return false\"><img "];
348 [_resp appendContentString:@"alt=\""];
349 [_resp appendContentString:label];
350 [_resp appendContentString:@"\" title=\""];
351 [_resp appendContentString:label];
352 [_resp appendContentString:@"\" "];
354 [_resp appendContentString:@"border=\"0\" name=\"imEx\" src=\""];
355 [_resp appendContentString:img];
356 [_resp appendContentString:@"\" /></a>"];
359 - (void)_appendLinkWithFragmentID:(NSString *)fragId
360 label:(NSString *)label imageURL:(NSString *)img
361 isCollapsed:(BOOL)isCollapsed
362 toResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx
364 [_resp appendContentString:@"<a href=\""];
365 [_resp appendContentString:[_ctx componentActionURL]];
366 if ([fragId length] > 0) {
367 [_resp appendContentString:@"#"];
368 [_resp appendContentString:fragId];
370 [_resp appendContentString:@"\">"];
373 [_resp appendContentString:@"<img border=\"0\" src=\""];
374 [_resp appendContentString:img];
375 [_resp appendContentString:@"\""];
377 [_resp appendContentString:@" alt=\""];
378 [_resp appendContentString:label];
379 [_resp appendContentString:@"\" title=\""];
380 [_resp appendContentString:label];
381 [_resp appendContentString:@"\""];
383 [_resp appendContentString:@" />"];
386 [_resp appendContentString:(isCollapsed) ? @"[+]" : @"[-]"];
388 [_resp appendContentString:@"</a>"];
391 - (void)appendToResponse:(WOResponse *)_resp inContext:(WOContext *)_ctx {
392 /* TODO: split up this huge method */
398 NSString *label = nil;
399 NSString *fragId = nil;
400 NSString *scriptId = nil;
402 comp = [_ctx component];
403 fragId = [self->fragmentIdentifier stringValueInComponent:comp];
405 isCollapsed = [[_ctx objectForKey:WECollapsible_IsCollapsed] boolValue];
406 scriptId = [_ctx objectForKey:WECollapsible_ScriptId];
407 doScript = [scriptId length] > 0 ? YES : NO;
410 ? [self->closedImageFileName stringValueInComponent:comp]
411 : [self->openedImageFileName stringValueInComponent:comp];
413 label = (isCollapsed)
414 ? [self->closedLabel stringValueInComponent:comp]
415 : [self->openedLabel stringValueInComponent:comp];
417 img = WEUriOfResource(img, _ctx);
421 [_resp appendContentString:@" "];
424 doForm = ([_ctx isInForm] && self->submitActionName && img);
426 [_ctx appendElementIDComponent:(isCollapsed) ? @"c" : @"e"];
428 [self _appendScriptWithID:scriptId label:label imageURL:img
429 toResponse:_resp inContext:_ctx];
432 [_resp appendContentString:@"<input type=\"image\" border=\"0\" name=\""];
433 [_resp appendContentString:[_ctx elementID]];
434 [_resp appendContentString:@"\" src=\""];
435 [_resp appendContentString:img];
436 [_resp appendContentString:@"\" />"];
439 [self _appendLinkWithFragmentID:fragId label:label imageURL:img
440 isCollapsed:isCollapsed
441 toResponse:_resp inContext:_ctx];
445 WEClientCapabilities *ccaps;
447 ccaps = [[_ctx request] clientCapabilities];
449 [_resp appendContentString:@"<a name=\""];
450 [_resp appendContentString:fragId];
451 [_resp appendContentString:@"\"> </a>"];
452 if ([self->isClicked boolValueInComponent:comp] &&
453 ([ccaps isInternetExplorer] || [ccaps isMozilla] || [ccaps isNetscape6])) {
454 if ([self->isClicked isValueSettable])
455 [self->isClicked setBoolValue:NO inComponent:comp];
457 [_resp appendContentString:
458 [NSString stringWithFormat:
459 @"\n<script language=\"JavaScript\">\n"
461 @" window.location.hash=\"#%@\";\n"
468 [_resp appendContentString:@" "];
470 if (label && !doScript) {
472 [_resp appendContentString:@"<a href=\""];
473 [_resp appendContentString:[_ctx componentActionURL]];
475 [_resp appendContentString:@"#"];
476 [_resp appendContentString:fragId];
478 [_resp appendContentString:@"\">"];
481 [_resp appendContentString:label];
484 [_resp appendContentString:@"</a>"];
486 [_ctx deleteLastElementIDComponent];
489 @end /* WECollapsibleAction */
491 @interface WECollapsibleTitleMode: WEContextConditional
494 @implementation WECollapsibleTitleMode
496 - (NSString *)_contextKey {
497 return WECollapsible_TitleMode;
500 @end /* WECollapsibleTitleMode */
502 @interface WECollapsibleContentMode: WEContextConditional
505 @implementation WECollapsibleContentMode
507 - (NSString *)_contextKey {
508 return WECollapsible_ContentMode;
511 @end /* WECollapsibleContentMode */