1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include "Wt/WAnchor.h"
7 #include "Wt/WApplication.h"
8 #include "Wt/WEnvironment.h"
9 #include "Wt/WImage.h"
10 #include "Wt/WResource.h"
11 #include "Wt/WText.h"
12 
13 #include "DomElement.h"
14 #include "WebUtils.h"
15 
16 namespace Wt {
17 
LinkState()18 WAnchor::LinkState::LinkState()
19   : clickJS(nullptr)
20 { }
21 
~LinkState()22 WAnchor::LinkState::~LinkState()
23 {
24   delete clickJS;
25 }
26 
WAnchor()27 WAnchor::WAnchor()
28 {
29   setInline(true);
30 }
31 
WAnchor(const WLink & link)32 WAnchor::WAnchor(const WLink& link)
33 {
34   setInline(true);
35   setLink(link);
36 }
37 
WAnchor(const WLink & link,const WString & text)38 WAnchor::WAnchor(const WLink& link, const WString& text)
39 {
40   setInline(true);
41   setLink(link);
42   text_ = new WText(text);
43   addWidget(std::unique_ptr<WWidget>(text_.get()));
44 }
45 
WAnchor(const WLink & link,std::unique_ptr<WImage> image)46 WAnchor::WAnchor(const WLink& link, std::unique_ptr<WImage> image)
47 {
48   setInline(true);
49   setLink(link);
50 
51   if (image) {
52     image_ = image.get();
53     addWidget(std::move(image));
54   }
55 }
56 
setLink(const WLink & link)57 void WAnchor::setLink(const WLink& link)
58 {
59   if (linkState_.link.type() != LinkType::Resource &&
60       linkState_.link == link)
61     return;
62 
63   linkState_.link = link;
64 
65   flags_.set(BIT_LINK_CHANGED);
66 
67   repaint();
68 
69   switch (linkState_.link.type()) {
70   case LinkType::Resource:
71     linkState_.link.resource()->dataChanged().connect
72       (this, &WAnchor::resourceChanged);
73     break;
74   case LinkType::InternalPath:
75     WApplication::instance()->enableInternalPaths();
76     break;
77   default:
78     break;
79   }
80 }
81 
text()82 const WString& WAnchor::text() const
83 {
84   static WString empty("");
85   if (text_)
86     return text_->text();
87   else
88     return empty;
89 }
90 
setText(const WString & text)91 void WAnchor::setText(const WString& text)
92 {
93   if (!text_) {
94     std::unique_ptr<WText> t(new WText(text));
95     text_ = t.get();
96     addWidget(std::move(t));
97   } else
98     text_->setText(text);
99 }
100 
setWordWrap(bool wordWrap)101 void WAnchor::setWordWrap(bool wordWrap)
102 {
103   if (!text_)
104     setText(WString());
105 
106   text_->setWordWrap(wordWrap);
107 }
108 
wordWrap()109 bool WAnchor::wordWrap() const
110 {
111   return text_ ? text_->wordWrap() : true;
112 }
113 
setTextFormat(TextFormat textFormat)114 void WAnchor::setTextFormat(TextFormat textFormat)
115 {
116   if (!text_)
117     setText(WString());
118 
119   text_->setTextFormat(textFormat);
120 }
121 
textFormat()122 TextFormat WAnchor::textFormat() const
123 {
124   return text_ ? text_->textFormat() : TextFormat::XHTML;
125 }
126 
setImage(std::unique_ptr<WImage> image)127 void WAnchor::setImage(std::unique_ptr<WImage> image)
128 {
129   image_ = image.get();
130 
131   if (image)
132     addWidget(std::move(image));
133 }
134 
resourceChanged()135 void WAnchor::resourceChanged()
136 {
137   flags_.set(BIT_LINK_CHANGED);
138   repaint();
139 }
140 
enableAjax()141 void WAnchor::enableAjax()
142 {
143   if (linkState_.link.type() == LinkType::InternalPath) {
144     flags_.set(BIT_LINK_CHANGED);
145     repaint();
146   }
147 
148   WContainerWidget::enableAjax();
149 }
150 
canReceiveFocus()151 bool WAnchor::canReceiveFocus() const
152 {
153   return true;
154 }
155 
tabIndex()156 int WAnchor::tabIndex() const
157 {
158   int result = WContainerWidget::tabIndex();
159 
160   if (result == std::numeric_limits<int>::min())
161     return 0;
162   else
163     return result;
164 }
165 
setFirstFocus()166 bool WAnchor::setFirstFocus()
167 {
168   return false;
169 }
170 
updateDom(DomElement & element,bool all)171 void WAnchor::updateDom(DomElement& element, bool all)
172 {
173   bool needsUrlResolution = false;
174 
175   if (flags_.test(BIT_LINK_CHANGED) || all) {
176     needsUrlResolution = renderHRef(this, linkState_, element);
177     flags_.reset(BIT_LINK_CHANGED);
178   }
179 
180   if (flags_.test(BIT_TARGET_CHANGED) || all) {
181 	renderHTarget(linkState_, element, all);
182 
183 	/*
184 	 * TODO(Benoit)
185 	 * We do that here because of the static method,
186 	 * We should maybe move the code to renderHTarget()
187 	 * and make it non static ?
188 	 */
189 	flags_.reset(BIT_TARGET_CHANGED);
190   }
191 
192   WContainerWidget::updateDom(element, all);
193 
194   if (needsUrlResolution)
195     renderUrlResolution(this, element, all);
196 }
197 
renderHRef(WInteractWidget * widget,LinkState & linkState,DomElement & element)198 bool WAnchor::renderHRef(WInteractWidget *widget,
199 			 LinkState& linkState, DomElement& element)
200 {
201   WApplication *app = WApplication::instance();
202 
203   if (linkState.link.isNull() || widget->isDisabled())
204     element.removeAttribute("href");
205   else {
206     std::string url = linkState.link.resolveUrl(app);
207 
208     /*
209      * From 但浩亮: setRefInternalPath() and setTarget(LinkTarget::NewWindow)
210      * does not work without the check below:
211      */
212     if (linkState.link.target() == LinkTarget::Self) {
213       linkState.clickJS
214 	= linkState.link.manageInternalPathChange(app, widget,
215 						  linkState.clickJS);
216     } else {
217       delete linkState.clickJS;
218       linkState.clickJS = nullptr;
219     }
220 
221     url = app->encodeUntrustedUrl(url);
222 
223     std::string href = url;
224     element.setAttribute("href", href);
225     return !app->environment().internalPathUsingFragments()
226       && href.find("://") == std::string::npos && href[0] != '/';
227   }
228 
229   return false;
230 }
231 
renderHTarget(LinkState & linkState,DomElement & element,bool all)232 void WAnchor::renderHTarget(LinkState& linkState, DomElement& element, bool all)
233 {
234   switch (linkState.link.target()) {
235   case LinkTarget::Self:
236     if (!all)
237       element.setProperty(Property::Target, "_self");
238     break;
239   case LinkTarget::ThisWindow:
240     element.setProperty(Property::Target, "_top");
241     break;
242   case LinkTarget::NewWindow:
243     element.setProperty(Property::Target, "_blank");
244     break;
245   case LinkTarget::Download:
246     element.setProperty(Property::Target, "wt_iframe_dl");
247     element.setProperty(Property::Download, ""); // Only works on some browsers (FF, Chrome)
248     break;
249   }
250 }
251 
renderUrlResolution(WWidget * widget,DomElement & element,bool all)252 void WAnchor::renderUrlResolution(WWidget *widget, DomElement& element,
253 				  bool all)
254 {
255   if (all)
256     element.setProperty(Property::Class,
257 			Utils::addWord(widget->styleClass().toUTF8(), "Wt-rr"));
258   else
259     element.callJavaScript("$('#" + widget->id() + "').addClass('Wt-rr');");
260 }
261 
propagateRenderOk(bool deep)262 void WAnchor::propagateRenderOk(bool deep)
263 {
264   flags_.reset(BIT_LINK_CHANGED);
265   flags_.reset(BIT_TARGET_CHANGED);
266 
267   WContainerWidget::propagateRenderOk(deep);
268 }
269 
propagateSetEnabled(bool enabled)270 void WAnchor::propagateSetEnabled(bool enabled)
271 {
272   WContainerWidget::propagateSetEnabled(enabled);
273 
274   resourceChanged();
275 }
276 
domElementType()277 DomElementType WAnchor::domElementType() const
278 {
279   return DomElementType::A;
280 }
281 
282 }
283