2002-07-08 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / Control.cs
1 //\r
2 // System.Web.UI.Control.cs\r
3 //\r
4 // Author:\r
5 //   Bob Smith <bob@thestuff.net>\r
6 //\r
7 // (C) Bob Smith\r
8 //\r
9 \r
10 /*\r
11  * Maintainer: bob@thestuff.net, gvaish@iitk.ac.in\r
12  * (C) Bob Smith, Gaurav Vaish\r
13  */\r
14 \r
15 //notes: view state only tracks changes after OnInit method is executed for the page request. You can read from it at any time, but cant write to it during rendering.\r
16 //even more notes: view state info in trackviewstate method description. read later.\r
17 //Ok, enough notes: what the heck is different between enable view state, and track view state.\r
18 //Well, maybe not. How does the ViewState know when to track changes? Does it look at the property\r
19 //on the owning control, or does it have a method/property of its own that gets called?\r
20 // I think this last question is solved in the Interface for it. Look into this.\r
21 \r
22 //cycle:\r
23 //init is called when control is first created.\r
24 //load view state ic called right after init to populate the view state.\r
25 //loadpostdata is called if ipostbackdatahandler is implemented.\r
26 //load is called when control is loaded into a page\r
27 //raisepostdatachangedevent if ipostbackdatahandler is implemented.\r
28 //raisepostbackevent if ipostbackeventhandler is implemented.\r
29 //prerender is called when the server is about to render its page object\r
30 //SaveViewState is called.\r
31 //Unload then dispose it apears. :)\r
32 \r
33 //Naming Container MUST have some methods. What are they? No clue. Help?\r
34 \r
35 //read this later. http://gotdotnet.com/quickstart/aspplus/\r
36 //This to: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconattributesdesign-timesupport.asp\r
37 //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpcontracefunctionality.asp\r
38 \r
39 // Isnt life grand? :)\r
40 // See the undocumented methods? Gota love um. ;)\r
41 // ASP.test4_aspx.Page_Load(Object Sender, EventArgs e) in \\genfs2\www24\bobsmith11\test4.aspx:6\r
42 // System.Web.UI.Control.OnLoad(EventArgs e) +67\r
43 // System.Web.UI.Control.LoadRecursive() +73\r
44 // System.Web.UI.Page.ProcessRequestMain() +394\r
45 \r
46 // ASP.test4_aspx.Page_Unload(Object Sender, EventArgs e) in \\genfs2\www24\bobsmith11\test4.aspx:6\r
47 // System.EventHandler.Invoke(Object sender, EventArgs e) +0\r
48 // System.Web.UI.Control.OnUnload(EventArgs e) +67\r
49 // System.Web.UI.Control.UnloadRecursive(Boolean dispose) +78\r
50 // System.Web.UI.Page.ProcessRequest() +194\r
51 // System.Web.UI.Page.ProcessRequest(HttpContext context) +18\r
52 // System.Web.CallHandlerExecutionStep.Execute() +179\r
53 // System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +87\r
54 \r
55 \r
56 // ASP.test4_aspx.Page_Unload(Object Sender, EventArgs e) in \\genfs2\www24\bobsmith11\test4.aspx:6\r
57 // System.Web.UI.Control.OnUnload(EventArgs e) +67\r
58 // System.Web.UI.Control.UnloadRecursive(Boolean dispose) +78\r
59 // System.Web.UI.Page.ProcessRequest()\r
60 \r
61 // ASP.test4_aspx.Page_Kill(Object Sender, EventArgs e) in \\genfs2\www24\bobsmith11\test4.aspx:6\r
62 // System.Web.UI.Control.OnPreRender(EventArgs e) +67\r
63 // System.Web.UI.Control.PreRenderRecursiveInternal() +61\r
64 // System.Web.UI.Page.ProcessRequestMain() +753\r
65 \r
66 // ASP.test4_aspx.OnInit(EventArgs e) in \\genfs2\www24\bobsmith11\test4.aspx:6\r
67 // System.Web.UI.Control.InitRecursive(Control namingContainer) +202\r
68 // System.Web.UI.Page.ProcessRequestMain() +120\r
69 \r
70 // ASP.test4_aspx.SaveViewState() in \\genfs2\www24\bobsmith11\test4.aspx:12\r
71 // System.Web.UI.Control.SaveViewStateRecursive() +51\r
72 // System.Web.UI.Page.SavePageViewState() +174\r
73 // System.Web.UI.Page.ProcessRequestMain() +861\r
74 \r
75 // ASP.test_aspx.LoadViewState(Object t) +28\r
76 // System.Web.UI.Control.LoadViewStateRecursive(Object savedState) +125\r
77 // System.Web.UI.Page.LoadPageViewState() +182\r
78 // System.Web.UI.Page.ProcessRequestMain() +256\r
79 \r
80 using System;\r
81 using System.Collections;\r
82 using System.Web;\r
83 using System.ComponentModel;\r
84 \r
85 namespace System.Web.UI\r
86 {\r
87         public class Control : IComponent, IDisposable, IParserAccessor, IDataBindingsAccessor\r
88         {\r
89                 private static readonly object DataBindingEvent = new object();\r
90                 private static readonly object DisposedEvent = new object();\r
91                 private static readonly object InitEvent = new object();\r
92                 private static readonly object LoadEvent = new object();\r
93                 private static readonly object PreRenderEvent = new object();\r
94                 private static readonly object UnloadEvent = new object();\r
95                 private string _userId = null;\r
96                 private string _cachedUserId = null;\r
97                 private string _cachedClientId = null;\r
98                 private ControlCollection _controls = null;\r
99                 private bool _enableViewState = true;\r
100                 private IDictionary _childViewStates = null; //TODO: Not sure datatype. Placeholder guess.\r
101                 private bool _isNamingContainer = false;\r
102                 private Control _namingContainer = null;\r
103                 private Page _page = null;\r
104                 private Control _parent = null;\r
105                 private ISite _site = null;\r
106                 private bool _visible = true;\r
107                 private HttpContext _context = null;\r
108                 private bool _childControlsCreated = false;\r
109                 private StateBag _viewState = null;\r
110                 private bool _trackViewState = false;\r
111                 private EventHandlerList _events = new EventHandlerList();\r
112                 private RenderMethod _renderMethodDelegate = null;\r
113                 private bool autoID = true;\r
114                 private bool creatingControls = false;\r
115                 \r
116                     private DataBindingCollection dataBindings = null;\r
117 \r
118                 public Control()\r
119                 {\r
120                         if (this is INamingContainer) _isNamingContainer = true;\r
121                 }\r
122 \r
123                 public Control BindingContainer\r
124                 {\r
125                         get {\r
126                                 Control container = NamingContainer;\r
127                                 if (_isNamingContainer)\r
128                                         container = container.BindingContainer;\r
129                                 return container;\r
130                         }\r
131                 }\r
132                 \r
133                 public virtual string ClientID //DIT\r
134                 {\r
135                         get\r
136                         {\r
137                                 if (_cachedUserId != null && _cachedClientId != null)\r
138                                         return _cachedClientId;\r
139                                 _cachedUserId = UniqueID.Replace(':', '_');\r
140                                 return _cachedUserId;\r
141                         }\r
142                 }\r
143                 public virtual ControlCollection Controls //DIT\r
144                 {\r
145                         get\r
146                         {\r
147                                 if (_controls == null) _controls = CreateControlCollection();\r
148                                 return _controls;\r
149                         }\r
150                 }\r
151                 public virtual bool EnableViewState //DIT\r
152                 {\r
153                         get\r
154                         {\r
155                                 return _enableViewState;\r
156                         }\r
157                         set\r
158                         {\r
159                                 _enableViewState = value;\r
160                         }\r
161                 }\r
162                 public virtual string ID\r
163                 {\r
164                         get //DIT\r
165                         {\r
166                                 return _userId;\r
167                         }\r
168                         set\r
169                         {\r
170                                 if (value == null || value == "") return;\r
171                                 _userId = value;\r
172                                 _cachedUserId = null;\r
173                                 //TODO: Some Naming Container stuff here I think.\r
174                         }\r
175                 }\r
176                 public virtual Control NamingContainer //DIT\r
177                 {\r
178                         get\r
179                         {\r
180                                 if (_namingContainer == null && _parent != null)\r
181                                 {\r
182                                         if (_parent._isNamingContainer == false)\r
183                                                 _namingContainer = _parent.NamingContainer;\r
184                                         else\r
185                                                 _namingContainer = _parent;\r
186                                 }\r
187                                 return _namingContainer;\r
188                         }\r
189                 }\r
190                 public virtual Page Page //DIT\r
191                 {\r
192                         get\r
193                         {\r
194                                 if (_page == null && _parent != null) _page = _parent.Page;\r
195                                 return _page;\r
196                         }\r
197                         set\r
198                         {\r
199                                 _page = value;\r
200                         }\r
201                 }\r
202                 public virtual Control Parent //DIT\r
203                 {\r
204                         get\r
205                         {\r
206                                 return _parent;\r
207                         }\r
208                 }\r
209                 public ISite Site //DIT\r
210                 {\r
211                         get\r
212                         {\r
213                                 return _site;\r
214                         }\r
215                         set\r
216                         {\r
217                                 _site = value;\r
218                         }\r
219                 }\r
220                 public virtual string TemplateSourceDirectory\r
221                 {\r
222                         get\r
223                         {\r
224                                 return Context.Request.ApplicationPath; //TODO: Dont think this is right.\r
225                         }\r
226                 }\r
227 \r
228                                 [MonoTODO]\r
229                 public virtual string UniqueID\r
230                 {\r
231                         get\r
232                         {\r
233                                 //TODO: Some Naming container methods here. What are they? Why arnt they declared?\r
234                                 //Note: Nuked the old stuff here. Was total crap. :)\r
235                                 return ID;\r
236                         }\r
237                 }\r
238                 public virtual bool Visible\r
239                 {\r
240                         get\r
241                         {\r
242                                 if (_visible == false)\r
243                                         return false;\r
244 \r
245                                 if (_parent != null)\r
246                                         return _parent.Visible;\r
247 \r
248                                 return true;\r
249                         }\r
250                         set\r
251                         {\r
252                                 _visible = value;\r
253                         }\r
254                 }\r
255                 protected bool ChildControlsCreated //DIT\r
256                 {\r
257                         get\r
258                         {\r
259                                 return _childControlsCreated;\r
260                         }\r
261                         set\r
262                         {\r
263                                 if (value == false && _childControlsCreated == true)\r
264                                         _controls.Clear();\r
265                                 _childControlsCreated = value;\r
266                         }\r
267                 }\r
268                 protected virtual HttpContext Context //DIT\r
269                 {\r
270                         get\r
271                         {\r
272                                 HttpContext context;\r
273                                 if (_context != null)\r
274                                         return _context;\r
275                                 if (_parent == null)\r
276                                         return HttpContext.Current;\r
277                                 context = _parent.Context;\r
278                                 if (context != null)\r
279                                         return context;\r
280                                 return HttpContext.Current;\r
281                         }\r
282                 }\r
283                 protected EventHandlerList Events //DIT\r
284                 {\r
285                         get\r
286                         {\r
287                                 if (_events == null)\r
288                                 {\r
289                                         _events = new EventHandlerList();\r
290                                 }\r
291                                 return _events;\r
292                         }\r
293                 }\r
294                 protected bool HasChildViewState //DIT\r
295                 {\r
296                         get\r
297                         {\r
298                                 if (_childViewStates == null) return false;\r
299                                 return true;\r
300                         }\r
301                 }\r
302                 protected bool IsTrackingViewState //DIT\r
303                 {\r
304                         get\r
305                         {\r
306                                 return _trackViewState;\r
307                         }\r
308                 }\r
309                 protected virtual StateBag ViewState\r
310                 {\r
311                         get\r
312                         {\r
313                                 if(_viewState == null)\r
314                                 {\r
315                                         _viewState = new StateBag(ViewStateIgnoresCase);\r
316                                 if(IsTrackingViewState)\r
317                                                 _viewState.TrackViewState();\r
318                                 }\r
319                                 return _viewState;\r
320                         }\r
321                 }\r
322                 protected virtual bool ViewStateIgnoresCase //DIT\r
323                 {\r
324                         get\r
325                         {\r
326                                 return true;\r
327                         }\r
328                 }\r
329 \r
330                 private int defaultNumberID;\r
331                 protected internal virtual void AddedControl (Control control, int index)\r
332                 {\r
333                         /* Ensure the control don't have more than 1 parent */\r
334                         if (control._parent != null)\r
335                                 control._parent.Controls.Remove (control);\r
336 \r
337                         control._parent = this;\r
338                         control._page = Page;\r
339 \r
340                         // Without this, DataBoundLiteralControl crashes in OnDataBound event.\r
341                         Control namingContainer = NamingContainer;\r
342                         if (namingContainer != null)\r
343                                 control._namingContainer = namingContainer;\r
344                         \r
345                         if (control.AutoID == true && control.ID == null)\r
346                                 control.ID = "_ctrl_" + defaultNumberID++;\r
347                 }\r
348 \r
349                 protected virtual void AddParsedSubObject(object obj) //DIT\r
350                 {\r
351                         Control c = (Control)obj;\r
352                         if (c != null) Controls.Add(c);\r
353                 }\r
354                 protected void BuildProfileTree(string parentId, bool calcViewState)\r
355                 {\r
356                         //TODO\r
357                 }\r
358                 protected void ClearChildViewState()\r
359                 {\r
360                         //TODO\r
361                         //Not quite sure about this. an example clears children then calls this, so I think\r
362                         //view state is local to the current object, not children.\r
363                 }\r
364                 protected virtual void CreateChildControls() {} //DIT\r
365                 protected virtual ControlCollection CreateControlCollection() //DIT\r
366                 {\r
367                         return new ControlCollection(this);\r
368                 }\r
369 \r
370                 protected virtual void EnsureChildControls () //DIT\r
371                 {\r
372                         if (ChildControlsCreated == false && !creatingControls) {\r
373                                 creatingControls = true;\r
374                                 CreateChildControls();\r
375                                 ChildControlsCreated = true;\r
376                                 creatingControls = false;\r
377                         }\r
378                 }\r
379 \r
380                 protected virtual Control FindControl(string id, int pathOffset)\r
381                 {\r
382                         //TODO: I think there is Naming Container stuff here. Redo.\r
383                         int i;\r
384                         Control ctrl;\r
385                         for (i = pathOffset; i < _controls.Count; i++){\r
386                                 ctrl = _controls [i];\r
387                                 \r
388                                 if (ctrl.ID == id)\r
389                                         return ctrl;\r
390 \r
391                                 if (ctrl.Controls.Count > 0){\r
392                                         Control other = ctrl.FindControl (id);\r
393                                         if (other != null)\r
394                                                 return other;\r
395                                 }\r
396                                 \r
397                         }\r
398                         return null;\r
399                 }\r
400 \r
401                 protected virtual void LoadViewState(object savedState)\r
402                 {\r
403                         ViewState.LoadViewState (savedState);\r
404                 }\r
405                 \r
406                 [MonoTODO]\r
407                 protected string MapPathSecure(string virtualPath)\r
408                 {\r
409                         //TODO: Need to read up on security+web.\r
410                         //Return the same path. So AdRotator can read its config file.\r
411                         return virtualPath;\r
412                 }\r
413                 protected virtual bool OnBubbleEvent(object source, EventArgs args) //DIT\r
414                 {\r
415                         return false;\r
416                 }\r
417                 protected virtual void OnDataBinding(EventArgs e) //DIT\r
418                 {\r
419                         if (_events != null)\r
420                         {\r
421                                 EventHandler eh = (EventHandler)(_events[DataBindingEvent]);\r
422                                 if (eh != null) eh(this, e);\r
423                         }\r
424                 }\r
425                 protected virtual void OnInit(EventArgs e) //DIT\r
426                 {\r
427                         if (_events != null)\r
428                         {\r
429                                 EventHandler eh = (EventHandler)(_events[InitEvent]);\r
430                                 if (eh != null) eh(this, e);\r
431                         }\r
432                 }\r
433                 protected virtual void OnLoad(EventArgs e) //DIT\r
434                 {\r
435                         if (_events != null)\r
436                         {\r
437                                 EventHandler eh = (EventHandler)(_events[LoadEvent]);\r
438                                 if (eh != null) eh(this, e);\r
439                         }\r
440                 }\r
441                 protected virtual void OnPreRender(EventArgs e) //DIT\r
442                 {\r
443                         if (_events != null)\r
444                         {\r
445                                 EventHandler eh = (EventHandler)(_events[PreRenderEvent]);\r
446                                 if (eh != null) eh(this, e);\r
447                         }\r
448                 }\r
449                 protected virtual void OnUnload(EventArgs e) //DIT\r
450                 {\r
451                         if (_events != null)\r
452                         {\r
453                                 EventHandler eh = (EventHandler)(_events[UnloadEvent]);\r
454                                 if (eh != null) eh(this, e);\r
455                         }\r
456                 }\r
457                 \r
458                                 [MonoTODO]\r
459                 protected void RaiseBubbleEvent(object source, EventArgs args)\r
460                 {\r
461                         throw new NotImplementedException();\r
462                         //return false;\r
463                 }\r
464                 protected virtual void Render(HtmlTextWriter writer) //DIT\r
465                 {\r
466                         RenderChildren(writer);\r
467                 }\r
468                 protected virtual void RenderChildren(HtmlTextWriter writer) //DIT\r
469                 {\r
470                         if (_renderMethodDelegate != null)\r
471                                 _renderMethodDelegate(writer, this);\r
472                         else if (_controls != null)\r
473                                 foreach (Control c in _controls)\r
474                                         c.RenderControl(writer);\r
475                 }\r
476                 \r
477                 protected virtual object SaveViewState ()\r
478                 {\r
479                         return ViewState.SaveViewState ();\r
480                 }\r
481 \r
482                 protected virtual void TrackViewState()\r
483                 {\r
484                         _trackViewState = true;\r
485                 }\r
486                 \r
487                                 [MonoTODO]\r
488                 public virtual void Dispose()\r
489                 {\r
490                         //TODO: nuke stuff.\r
491                         throw new NotImplementedException();\r
492                         /*\r
493                         if (_events != null)\r
494                         {\r
495                                 EventHandler eh = (EventHandler)(_events[DisposedEvent]);\r
496                                 if (eh != null) eh(this, e);\r
497                         }\r
498                     */\r
499                 }\r
500 \r
501                 public bool HasChildren\r
502                 {\r
503                         get { return (_controls != null && _controls.Count > 0); }\r
504                 }\r
505 \r
506                 public event EventHandler DataBinding //DIT\r
507                 {\r
508                         add\r
509                         {\r
510                                 Events.AddHandler(DataBindingEvent, value);\r
511                         }\r
512                         remove\r
513                         {\r
514                                 Events.RemoveHandler(DataBindingEvent, value);\r
515                         }\r
516                 }\r
517                 public event EventHandler Disposed //DIT\r
518                 {\r
519                         add\r
520                         {\r
521                                 Events.AddHandler(DisposedEvent, value);\r
522                         }\r
523                         remove\r
524                         {\r
525                                 Events.RemoveHandler(DisposedEvent, value);\r
526                         }\r
527                 }\r
528                 public event EventHandler Init //DIT\r
529                 {\r
530                         add\r
531                         {\r
532                                 Events.AddHandler(InitEvent, value);\r
533                         }\r
534                         remove\r
535                         {\r
536                                 Events.RemoveHandler(InitEvent, value);\r
537                         }\r
538                 }\r
539                 public event EventHandler Load //DIT\r
540                 {\r
541                         add\r
542                         {\r
543                                 Events.AddHandler(LoadEvent, value);\r
544                         }\r
545                         remove\r
546                         {\r
547                                 Events.RemoveHandler(LoadEvent, value);\r
548                         }\r
549                 }\r
550                 public event EventHandler PreRender //DIT\r
551                 {\r
552                         add\r
553                         {\r
554                                 Events.AddHandler(PreRenderEvent, value);\r
555                         }\r
556                         remove\r
557                         {\r
558                                 Events.RemoveHandler(PreRenderEvent, value);\r
559                         }\r
560                 }\r
561                 public event EventHandler Unload //DIT\r
562                 {\r
563                         add\r
564                         {\r
565                                 Events.AddHandler(UnloadEvent, value);\r
566                         }\r
567                         remove\r
568                         {\r
569                                 Events.RemoveHandler(UnloadEvent, value);\r
570                         }\r
571                 }\r
572                 public virtual void DataBind() //DIT\r
573                 {\r
574                         OnDataBinding(EventArgs.Empty);\r
575                         if (_controls != null)\r
576                                 foreach (Control c in _controls)\r
577                                         c.DataBind();\r
578                 }\r
579                 public virtual Control FindControl(string id) //DIT\r
580                 {\r
581                         return FindControl(id, 0);\r
582                 }\r
583                 public virtual bool HasControls() //DIT\r
584                 {\r
585                         if (_controls != null && _controls.Count >0) return true;\r
586                         return false;\r
587                 }\r
588                 public void RenderControl(HtmlTextWriter writer)\r
589                 {\r
590                         if (_visible)\r
591                         {\r
592                                 // By now, PreRender is fired here.\r
593                                 OnPreRender (EventArgs.Empty); //FIXME\r
594                                 //TODO: Something about tracing here.\r
595                                 Render(writer);\r
596                         }\r
597                 }\r
598                 \r
599                 [MonoTODO]\r
600                 public string ResolveUrl(string relativeUrl)\r
601                 {\r
602                         return relativeUrl;\r
603                 }\r
604                 public void SetRenderMethodDelegate(RenderMethod renderMethod) //DIT\r
605                 {\r
606                         _renderMethodDelegate = renderMethod;\r
607                 }\r
608                 protected void LoadRecursive()\r
609                 {\r
610                         OnLoad(EventArgs.Empty);\r
611                         if (_controls != null) foreach (Control c in _controls) c.LoadRecursive();\r
612                 }\r
613                 protected void UnloadRecursive(Boolean dispose)\r
614                 {\r
615                         OnUnload(EventArgs.Empty);\r
616                         if (_controls != null) foreach (Control c in _controls) c.UnloadRecursive(dispose);\r
617                         if (dispose) Dispose();\r
618                 }\r
619                 protected void PreRenderRecursiveInternal()\r
620                 {\r
621                         OnPreRender(EventArgs.Empty);\r
622                         if (_controls != null) foreach (Control c in _controls) c.PreRenderRecursiveInternal();\r
623                 }\r
624                 protected void InitRecursive(Control namingContainer)\r
625                 {\r
626                         if (_controls != null) foreach (Control c in _controls) c.InitRecursive(namingContainer);\r
627                         OnInit(EventArgs.Empty);\r
628                 }\r
629                 \r
630                 protected object SaveViewStateRecursive()\r
631                 {\r
632                         ArrayList controlList = new ArrayList ();\r
633                         ArrayList controlStates = new ArrayList ();\r
634 \r
635                         foreach (Control ctrl in Controls){\r
636                                 controlList.Add (ctrl);\r
637                                 controlStates.Add (ctrl.SaveViewStateRecursive ());\r
638                         }\r
639                                 \r
640                         return new Triplet (SaveViewState (), controlList, controlStates);\r
641                 }\r
642                 \r
643                 [MonoTODO]\r
644                 protected void LoadViewStateRecursive(Object savedState)\r
645                 {\r
646                         throw new NotImplementedException ();\r
647                 }\r
648                 \r
649                 void IParserAccessor.AddParsedSubObject(object obj)\r
650                 {\r
651                         AddParsedSubObject(obj);\r
652                 }\r
653                 \r
654                 DataBindingCollection IDataBindingsAccessor.DataBindings\r
655                 {\r
656                         get\r
657                         {\r
658                                 if(dataBindings == null)\r
659                                         dataBindings = new DataBindingCollection();\r
660                                 return dataBindings;\r
661                         }\r
662                 }\r
663                 \r
664                 bool IDataBindingsAccessor.HasDataBindings\r
665                 {\r
666                         get\r
667                         {\r
668                                 return (dataBindings!=null && dataBindings.Count>0);\r
669                         }\r
670                 }\r
671                 \r
672                 internal bool AutoID\r
673                 {\r
674                         get { return autoID; }\r
675                         set { autoID = value; }\r
676                 }\r
677 \r
678                 internal void PreventAutoID()\r
679                 {\r
680                         AutoID = false;\r
681                 }\r
682                 \r
683                 //TODO: I think there are some needed Interface implementations to do here.\r
684                 //TODO: Find api for INamingContainer.\r
685         }\r
686 }\r