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#import <NGObjWeb/WODynamicElement.h>
23
24/*
25  usage:
26
27     DropContainer: WEDropContainer {
28       elementName   = "tr";
29       isAttached    = YES;
30       tags          = ( * );
31       action        = dropAction;
32       droppedObject = droppedObject;
33
34       swapColor     = YES;
35       activeColor   = "lightblue";
36       bgColor       = bgColor;
37     }
38
39  renders this:
40
41    <$area BGCOLOR=white
42        onDragEnter="fnCancelDefault()"
43        onDragOver="fnCancelDefault()"
44        onDrop="fnGetInfo()">
45       $content
46    </$area>
47*/
48
49@interface WEDropContainer : WODynamicElement
50{
51  NSDictionary  *dExtraAttrs;
52  WOAssociation *tags;
53  WOAssociation *elementName;
54  WOAssociation *isAttached;  /* bool */
55  WOAssociation *effect;
56  WOAssociation *swapColor; /* swap bgcolor if drag object is over container */
57  WOAssociation *action;
58  WOAssociation *droppedObject;
59
60  WOElement     *template;
61}
62@end
63
64@interface WEDropScript : WODynamicElement
65+ (void)appendDropScriptToResponse:(WOResponse *)_response
66  inContext:(WOContext *)_ctx;
67@end
68
69#include <NGObjWeb/WEClientCapabilities.h>
70#include "common.h"
71
72@implementation WEDropContainer
73
74- (id)initWithName:(NSString *)_name
75  associations:(NSDictionary *)_config
76  template:(WOElement *)_subs
77{
78  if ((self = [super initWithName:_name associations:_config template:_subs])) {
79    self->tags            = WOExtGetProperty(_config, @"tags");
80    self->elementName     = WOExtGetProperty(_config, @"elementName");
81    self->effect          = WOExtGetProperty(_config, @"effect");
82    self->action          = WOExtGetProperty(_config, @"action");
83    self->droppedObject   = WOExtGetProperty(_config, @"droppedObject");
84    self->swapColor       = WOExtGetProperty(_config, @"swapColor");
85    self->isAttached      = WOExtGetProperty(_config, @"isAttached");
86
87    self->template = RETAIN(_subs);
88  }
89  return self;
90}
91
92- (void)dealloc {
93  RELEASE(self->dExtraAttrs);
94
95  RELEASE(self->swapColor);
96  RELEASE(self->droppedObject);
97  RELEASE(self->action);
98  RELEASE(self->template);
99  RELEASE(self->effect);
100  RELEASE(self->tags);
101  RELEASE(self->elementName);
102  RELEASE(self->isAttached);
103
104  [super dealloc];
105}
106
107- (void)setExtraAttributes:(NSDictionary *)_extras {
108  ASSIGNCOPY(self->dExtraAttrs, _extras);
109}
110
111- (void)appendExtraAttributesToResponse:(WOResponse *)_response
112  inContext:(WOContext *)_ctx
113{
114  if (self->dExtraAttrs) {
115    WOComponent  *sComponent;
116    NSEnumerator *keys;
117    NSString     *key;
118
119    sComponent = [_ctx component];
120
121    keys = [self->dExtraAttrs keyEnumerator];
122
123    while ((key = [keys nextObject])) {
124      id value;
125
126      value = [self->dExtraAttrs objectForKey:key];
127
128      if (value == nil)
129        continue;
130
131      //key   = [key lowercaseString];
132      value = [value stringValueInComponent:sComponent];
133
134      [_response appendContentCharacter:' '];
135      [_response appendContentString:key];
136      [_response appendContentString:@"=\""];
137      [_response appendContentHTMLAttributeValue:value];
138      [_response appendContentCharacter:'"'];
139    }
140  }
141}
142
143/* responder */
144
145- (void)takeValuesFromRequest:(WORequest *)_request
146  inContext:(WOContext *)_ctx
147{
148  [_ctx appendElementIDComponent:@"p"];
149  [self->template takeValuesFromRequest:_request inContext:_ctx];
150  [_ctx deleteLastElementIDComponent];
151}
152
153- (id)invokeActionForRequest:(WORequest *)_request
154  inContext:(WOContext *)_ctx
155{
156  NSString *op;
157  id result;
158
159  op = [[_ctx currentElementID] stringValue];
160#if 0
161    NSLog(@"check DROP ACTION %@ ..", op);
162#endif
163
164  if ([op isEqualToString:@"p"]) {
165    /* an action inside of the component */
166    [_ctx consumeElementID]; // consume id
167    [_ctx appendElementIDComponent:@"p"];
168    result = [self->template invokeActionForRequest:_request inContext:_ctx];
169    [_ctx deleteLastElementIDComponent];
170  }
171  else if ([op isEqualToString:@"drop"]) {
172    /* an action of *this* component */
173    [_ctx consumeElementID]; // consume id
174    [_ctx appendElementIDComponent:@"drop"];
175
176#if 0
177    NSLog(@"WILL RUN DROP ACTION ..");
178#endif
179
180    if ([self->droppedObject isValueSettable]) {
181      [self->droppedObject setValue:
182                             [_ctx valueForKey:@"WEDragContainer_DropObject"]
183                           inComponent:[_ctx component]];
184    }
185
186    result = [self->action valueInComponent:[_ctx component]];
187
188    [_ctx deleteLastElementIDComponent];
189  }
190  else {
191    NSLog(@"invalid element-id: did not expect '%@' id", op);
192    result = nil;
193  }
194  return result;
195}
196
197- (void)appendExtraAttributesAsJavaScriptToResponse:(WOResponse *)_response
198  inContext:(WOContext *)_ctx
199{
200  if (self->dExtraAttrs) {
201    WOComponent     *sComponent = [_ctx component];
202    NSEnumerator    *keyEnum;
203    NSString        *key;
204
205    [_response appendContentString:
206               @"\n"
207               @"<script language=\"JavaScript\">\n"
208               @"<!--\n"];
209
210    keyEnum = [self->dExtraAttrs keyEnumerator];
211    while ((key = [keyEnum nextObject])) {
212      id value;
213
214      value = [self->dExtraAttrs objectForKey:key];
215
216      if (value == nil)
217        continue;
218
219      if ([[key lowercaseString] isEqualToString:@"width"])
220        key = @"width";
221      else if ([[key lowercaseString] isEqualToString:@"height"])
222        key = @"height";
223      else if ([[key lowercaseString] isEqualToString:@"bgcolor"])
224        key = @"bgColor";
225      else if ([[key lowercaseString] isEqualToString:@"activecolor"])
226        key = @"activeColor";
227      else if ([[key lowercaseString] isEqualToString:@"align"])
228        key = @"align";
229      else if ([[key lowercaseString] isEqualToString:@"valign"])
230        key = @"vAlign";
231      else if ([[key lowercaseString] isEqualToString:@"colspan"])
232        key = @"colSpan";
233      else if ([[key lowercaseString] isEqualToString:@"rowspan"])
234        key = @"rowSpan";
235      else if ([[key lowercaseString] isEqualToString:@"nowrap"])
236        key = @"noWrap";
237
238      value = [value stringValueInComponent:sComponent];
239      [_response appendContentString:
240                 [NSString stringWithFormat:@"tmp.%@ = \"%@\";\n", key, value]];
241    }
242    [_response appendContentString:
243               @"// -->\n"
244               @"</script>\n"];
245  }
246}
247
248- (void)appendToResponse:(WOResponse *)_response
249  inContext:(WOContext *)_ctx
250{
251  NSString *htmlEl;
252  NSString *ttag;
253  NSString *teffect;
254  NSString *containerID = nil;
255  BOOL     doDnD, doSwap, doAttach;
256
257  if ([_ctx isRenderingDisabled]) {
258    [self->template appendToResponse:_response inContext:_ctx];
259    return;
260  }
261
262  doDnD = [[[_ctx request] clientCapabilities] doesSupportDHTMLDragAndDrop];
263
264  //doDnD = YES;
265
266  ttag = [[self->tags valueInComponent:[_ctx component]]
267                      componentsJoinedByString:@","];
268
269  teffect = [self->effect stringValueInComponent:[_ctx component]];
270  if (teffect == nil) teffect = @"move";
271
272  [WEDropScript appendDropScriptToResponse:_response inContext:_ctx];
273
274  [_ctx appendElementIDComponent:@"drop"];
275
276  htmlEl   = [self->elementName stringValueInComponent:[_ctx component]];
277  doAttach = ([self->isAttached boolValueInComponent:[_ctx component]] &&
278             (self->elementName != nil));
279
280  if (htmlEl == nil || doAttach) htmlEl = @"span";
281
282  if ((self->elementName != nil) || doDnD) {
283    int containerIDc;
284
285    [_response appendContentString: @"<"];
286    [_response appendContentString:htmlEl];
287
288    if (doDnD) {
289      /* gen unique container id */
290
291      if ((containerID = [_ctx objectForKey:@"WEDropContainerSequence"])) {
292        containerID = AUTORELEASE([containerID copy]);
293        containerIDc = [containerID intValue];
294      }
295      else {
296        containerID  = @"0";
297        containerIDc = 0;
298      }
299
300      [_response appendContentString:@" id=\"skydrop"];
301      if (doAttach) [_response appendContentString:@"dummy"];
302      [_response appendContentString:containerID];
303      [_response appendContentString:@"\""];
304
305      containerIDc++;
306
307      [_ctx takeValue:[NSString stringWithFormat:@"%i", containerIDc]
308            forKey:@"WEDropContainerSequence"];
309    }
310  }
311
312  if (doDnD) {
313    NSString *swapOnAction  = @"";
314    NSString *swapOffAction = @"";
315    NSString *cancelAction;
316    NSString *infoAction;
317
318
319    doSwap = self->swapColor
320      ? [self->swapColor boolValueInComponent:[_ctx component]]
321      : YES;
322
323    cancelAction = [NSString stringWithFormat:@"fnCancelDefault('%@','%@');",
324                             ttag, teffect];
325    if (doSwap) {
326      swapOnAction  = [NSString stringWithFormat:
327                                @"dropFieldSwapColor(skydrop%@,true);",
328                                containerID];
329      swapOffAction = [NSString stringWithFormat:
330                                @"dropFieldSwapColor(skydrop%@,false);",
331                                containerID];
332    }
333
334    infoAction = [NSString stringWithFormat:@"fnGetInfo(this, '%@');",
335                           [_ctx componentActionURL]];
336
337    if (doAttach) {
338      NSString *tagName = nil;
339
340      // doAttach==YES -> self->elementName != nil
341      tagName = [self->elementName stringValueInComponent:[_ctx component]];
342      tagName = [tagName uppercaseString];
343
344      [_response appendContentCharacter:'>'];
345
346      [_response appendContentString:[NSString stringWithFormat:
347           @"\n"
348           @"<script language=\"JavaScript\">\n"
349           @"<!--\n"
350           @"  function onDragEnterFunc%@() {\n    %@\n     %@\n}\n"
351           @"  function onDragOverFunc%@()  {\n    %@\n     %@\n}\n"
352           @"  function onDragLeaveFunc%@() {\n %@\n     }\n"
353           @"  function onDropFunc%@()      {\n %@\n     }\n"
354           @"\n"
355           @"  tmp = document.getElementById(\"%@\");\n"
356           @"  i = 5;\n"
357           @"  while ((tmp.tagName != \"%@\") && (i >= 0)) {\n"
358           @"    tmp = tmp.parentNode;\n"
359           @"    i--;\n"
360           @"  }\n"
361           @"  tmp.ondragover    = onDragOverFunc%@;\n"
362           @"  tmp.ondragleave   = onDragLeaveFunc%@;\n"
363           @"  tmp.ondrop        = onDropFunc%@;\n"
364           @"  tmp.ondragenter   = onDragEnterFunc%@;\n"
365           @"  tmp.id            = \"%@\";\n"
366           @"// -->\n"
367           @"</script>\n",
368           containerID, cancelAction, swapOnAction,
369           containerID, cancelAction, swapOnAction,
370           containerID, swapOffAction,
371           containerID, infoAction,
372           [@"skydropdummy" stringByAppendingString:containerID],
373           tagName,
374           containerID,
375           containerID,
376           containerID,
377           containerID,
378           [@"skydrop" stringByAppendingString:containerID]]];
379
380      // remove bgColors of TR's sub TD:
381      if ([tagName isEqualToString:@"TR"]) {
382        [_response appendContentString:
383           @"\n"
384           @"<script language=\"JavaScript\">\n"
385           @"<!--\n"
386           @"list = tmp.getElementsByTagName(\"TD\");\n"
387           @"cnt  = list.length;\n"
388           @"for (i=0; i<cnt; i++) {\n"
389           @"  list[i].removeAttribute(\"bgColor\");\n"
390           @"}\n"
391           @"// -->\n"
392           @"</script>\n"];
393      }
394
395      /* append extra attributes by script */
396      [self appendExtraAttributesAsJavaScriptToResponse:_response
397                                              inContext:_ctx];
398    }
399    else {
400      [_response appendContentString:@" onDragEnter=\""];
401      [_response appendContentString:cancelAction];
402      [_response appendContentString:swapOnAction];
403
404      [_response appendContentString:@"\" onDragOver=\""];
405      [_response appendContentString:cancelAction];
406      [_response appendContentString:swapOnAction];
407
408      [_response appendContentString:@"\" onDragLeave=\""];
409      [_response appendContentString:swapOffAction];
410
411      [_response appendContentString:@"\" onDrop=\""];
412      [_response appendContentString:infoAction];
413      [_response appendContentCharacter:'"'];
414
415      [self appendExtraAttributesToResponse:_response inContext:_ctx];
416      [_response appendContentCharacter:'>'];
417    }
418  }
419  else if (self->elementName != nil) {
420    [self appendExtraAttributesToResponse:_response inContext:_ctx];
421    [_response appendContentCharacter:'>'];
422  }
423
424  [_ctx deleteLastElementIDComponent];
425
426  /* add template */
427  [_ctx appendElementIDComponent:@"p"];
428  [self->template appendToResponse:_response inContext:_ctx];
429  [_ctx deleteLastElementIDComponent];
430
431  /* close container */
432  if ((self->elementName != nil) || doDnD) {
433    [_response appendContentString:@"</"];
434    [_response appendContentString:htmlEl];
435    [_response appendContentCharacter:'>'];
436  }
437}
438
439/* accessors */
440
441- (id)template {
442  return self->template;
443}
444
445@end /* WEDropContainer */
446
447
448@implementation WEDropScript
449
450static NSString *dropScript =
451#include "WEDropScript.jsm"
452;
453
454+ (void)appendDropScriptToResponse:(WOResponse *)_response
455  inContext:(WOContext *)_ctx
456{
457  BOOL doDnD;
458
459  doDnD = [[[_ctx request] clientCapabilities] doesSupportDHTMLDragAndDrop];
460
461  if (![[_ctx objectForKey:@"WEDropContainerScriptDone"] boolValue] && doDnD) {
462    [_response appendContentString:dropScript];
463
464    [_ctx takeValue:[NSNumber numberWithBool:YES]
465          forKey:@"WEDropContainerScriptDone"];
466  }
467}
468
469- (void)appendToResponse:(WOResponse *)_response
470  inContext:(WOContext *)_ctx
471{
472  [[self class] appendDropScriptToResponse:_response inContext:_ctx];
473}
474
475@end /* WEDropScript */
476