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