Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/editor/plugins/linkdialogplugin.js
2868 views
1
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview A plugin for the LinkDialog.
17
*
18
* @author [email protected] (Nick Santos)
19
* @author [email protected] (Robby Walker)
20
*/
21
22
goog.provide('goog.editor.plugins.LinkDialogPlugin');
23
24
goog.require('goog.array');
25
goog.require('goog.dom');
26
goog.require('goog.editor.Command');
27
goog.require('goog.editor.plugins.AbstractDialogPlugin');
28
goog.require('goog.events.EventHandler');
29
goog.require('goog.functions');
30
goog.require('goog.ui.editor.AbstractDialog');
31
goog.require('goog.ui.editor.LinkDialog');
32
goog.require('goog.uri.utils');
33
34
35
36
/**
37
* A plugin that opens the link dialog.
38
* @constructor
39
* @extends {goog.editor.plugins.AbstractDialogPlugin}
40
*/
41
goog.editor.plugins.LinkDialogPlugin = function() {
42
goog.editor.plugins.LinkDialogPlugin.base(
43
this, 'constructor', goog.editor.Command.MODAL_LINK_EDITOR);
44
45
/**
46
* Event handler for this object.
47
* @type {goog.events.EventHandler<!goog.editor.plugins.LinkDialogPlugin>}
48
* @private
49
*/
50
this.eventHandler_ = new goog.events.EventHandler(this);
51
52
53
/**
54
* A list of whitelisted URL schemes which are safe to open.
55
* @type {Array<string>}
56
* @private
57
*/
58
this.safeToOpenSchemes_ = ['http', 'https', 'ftp'];
59
};
60
goog.inherits(
61
goog.editor.plugins.LinkDialogPlugin,
62
goog.editor.plugins.AbstractDialogPlugin);
63
64
65
/**
66
* Link object that the dialog is editing.
67
* @type {goog.editor.Link}
68
* @protected
69
*/
70
goog.editor.plugins.LinkDialogPlugin.prototype.currentLink_;
71
72
73
/**
74
* Optional warning to show about email addresses.
75
* @type {goog.html.SafeHtml}
76
* @private
77
*/
78
goog.editor.plugins.LinkDialogPlugin.prototype.emailWarning_;
79
80
81
/**
82
* Whether to show a checkbox where the user can choose to have the link open in
83
* a new window.
84
* @type {boolean}
85
* @private
86
*/
87
goog.editor.plugins.LinkDialogPlugin.prototype.showOpenLinkInNewWindow_ = false;
88
89
90
/**
91
* Whether the "open link in new window" checkbox should be checked when the
92
* dialog is shown, and also whether it was checked last time the dialog was
93
* closed.
94
* @type {boolean}
95
* @private
96
*/
97
goog.editor.plugins.LinkDialogPlugin.prototype.isOpenLinkInNewWindowChecked_ =
98
false;
99
100
101
/**
102
* Weather to show a checkbox where the user can choose to add 'rel=nofollow'
103
* attribute added to the link.
104
* @type {boolean}
105
* @private
106
*/
107
goog.editor.plugins.LinkDialogPlugin.prototype.showRelNoFollow_ = false;
108
109
110
/**
111
* Whether to stop referrer leaks. Defaults to false.
112
* @type {boolean}
113
* @private
114
*/
115
goog.editor.plugins.LinkDialogPlugin.prototype.stopReferrerLeaks_ = false;
116
117
118
/**
119
* Whether to block opening links with a non-whitelisted URL scheme.
120
* @type {boolean}
121
* @private
122
*/
123
goog.editor.plugins.LinkDialogPlugin.prototype.blockOpeningUnsafeSchemes_ =
124
true;
125
126
127
/** @override */
128
goog.editor.plugins.LinkDialogPlugin.prototype.getTrogClassId =
129
goog.functions.constant('LinkDialogPlugin');
130
131
132
/**
133
* Tells the plugin whether to block URLs with schemes not in the whitelist.
134
* If blocking is enabled, this plugin will stop the 'Test Link' popup
135
* window from being created. Blocking doesn't affect link creation--if the
136
* user clicks the 'OK' button with an unsafe URL, the link will still be
137
* created as normal.
138
* @param {boolean} blockOpeningUnsafeSchemes Whether to block non-whitelisted
139
* schemes.
140
*/
141
goog.editor.plugins.LinkDialogPlugin.prototype.setBlockOpeningUnsafeSchemes =
142
function(blockOpeningUnsafeSchemes) {
143
this.blockOpeningUnsafeSchemes_ = blockOpeningUnsafeSchemes;
144
};
145
146
147
/**
148
* Sets a whitelist of allowed URL schemes that are safe to open.
149
* Schemes should all be in lowercase. If the plugin is set to block opening
150
* unsafe schemes, user-entered URLs will be converted to lowercase and checked
151
* against this list. The whitelist has no effect if blocking is not enabled.
152
* @param {Array<string>} schemes String array of URL schemes to allow (http,
153
* https, etc.).
154
*/
155
goog.editor.plugins.LinkDialogPlugin.prototype.setSafeToOpenSchemes = function(
156
schemes) {
157
this.safeToOpenSchemes_ = schemes;
158
};
159
160
161
/**
162
* Tells the dialog to show a checkbox where the user can choose to have the
163
* link open in a new window.
164
* @param {boolean} startChecked Whether to check the checkbox the first
165
* time the dialog is shown. Subesquent times the checkbox will remember its
166
* previous state.
167
*/
168
goog.editor.plugins.LinkDialogPlugin.prototype.showOpenLinkInNewWindow =
169
function(startChecked) {
170
this.showOpenLinkInNewWindow_ = true;
171
this.isOpenLinkInNewWindowChecked_ = startChecked;
172
};
173
174
175
/**
176
* Tells the dialog to show a checkbox where the user can choose to have
177
* 'rel=nofollow' attribute added to the link.
178
*/
179
goog.editor.plugins.LinkDialogPlugin.prototype.showRelNoFollow = function() {
180
this.showRelNoFollow_ = true;
181
};
182
183
184
/**
185
* Returns whether the"open link in new window" checkbox was checked last time
186
* the dialog was closed.
187
* @return {boolean} Whether the"open link in new window" checkbox was checked
188
* last time the dialog was closed.
189
*/
190
goog.editor.plugins.LinkDialogPlugin.prototype
191
.getOpenLinkInNewWindowCheckedState = function() {
192
return this.isOpenLinkInNewWindowChecked_;
193
};
194
195
196
/**
197
* Tells the plugin to stop leaking the page's url via the referrer header when
198
* the "test this link" link is clicked. When the user clicks on a link, the
199
* browser makes a request for the link url, passing the url of the current page
200
* in the request headers. If the user wants the current url to be kept secret
201
* (e.g. an unpublished document), the owner of the url that was clicked will
202
* see the secret url in the request headers, and it will no longer be a secret.
203
* Calling this method will not send a referrer header in the request, just as
204
* if the user had opened a blank window and typed the url in themselves.
205
*/
206
goog.editor.plugins.LinkDialogPlugin.prototype.stopReferrerLeaks = function() {
207
this.stopReferrerLeaks_ = true;
208
};
209
210
211
/**
212
* Sets the warning message to show to users about including email addresses on
213
* public web pages.
214
* @param {!goog.html.SafeHtml} emailWarning Warning message to show users about
215
* including email addresses on the web.
216
*/
217
goog.editor.plugins.LinkDialogPlugin.prototype.setEmailWarning = function(
218
emailWarning) {
219
this.emailWarning_ = emailWarning;
220
};
221
222
223
/**
224
* Handles execCommand by opening the dialog.
225
* @param {string} command The command to execute.
226
* @param {*=} opt_arg {@link A goog.editor.Link} object representing the link
227
* being edited.
228
* @return {*} Always returns true, indicating the dialog was shown.
229
* @protected
230
* @override
231
*/
232
goog.editor.plugins.LinkDialogPlugin.prototype.execCommandInternal = function(
233
command, opt_arg) {
234
this.currentLink_ = /** @type {goog.editor.Link} */ (opt_arg);
235
return goog.editor.plugins.LinkDialogPlugin.base(
236
this, 'execCommandInternal', command, opt_arg);
237
};
238
239
240
/**
241
* Handles when the dialog closes.
242
* @param {goog.events.Event} e The AFTER_HIDE event object.
243
* @override
244
* @protected
245
*/
246
goog.editor.plugins.LinkDialogPlugin.prototype.handleAfterHide = function(e) {
247
goog.editor.plugins.LinkDialogPlugin.base(this, 'handleAfterHide', e);
248
this.currentLink_ = null;
249
};
250
251
252
/**
253
* @return {goog.events.EventHandler<T>} The event handler.
254
* @protected
255
* @this {T}
256
* @template T
257
*/
258
goog.editor.plugins.LinkDialogPlugin.prototype.getEventHandler = function() {
259
return this.eventHandler_;
260
};
261
262
263
/**
264
* @return {goog.editor.Link} The link being edited.
265
* @protected
266
*/
267
goog.editor.plugins.LinkDialogPlugin.prototype.getCurrentLink = function() {
268
return this.currentLink_;
269
};
270
271
272
/**
273
* Creates a new instance of the dialog and registers for the relevant events.
274
* @param {goog.dom.DomHelper} dialogDomHelper The dom helper to be used to
275
* create the dialog.
276
* @param {*=} opt_link The target link (should be a goog.editor.Link).
277
* @return {!goog.ui.editor.LinkDialog} The dialog.
278
* @override
279
* @protected
280
*/
281
goog.editor.plugins.LinkDialogPlugin.prototype.createDialog = function(
282
dialogDomHelper, opt_link) {
283
var dialog = new goog.ui.editor.LinkDialog(
284
dialogDomHelper,
285
/** @type {goog.editor.Link} */ (opt_link));
286
if (this.emailWarning_) {
287
dialog.setEmailWarning(this.emailWarning_);
288
}
289
if (this.showOpenLinkInNewWindow_) {
290
dialog.showOpenLinkInNewWindow(this.isOpenLinkInNewWindowChecked_);
291
}
292
if (this.showRelNoFollow_) {
293
dialog.showRelNoFollow();
294
}
295
dialog.setStopReferrerLeaks(this.stopReferrerLeaks_);
296
this.eventHandler_
297
.listen(dialog, goog.ui.editor.AbstractDialog.EventType.OK, this.handleOk)
298
.listen(
299
dialog, goog.ui.editor.AbstractDialog.EventType.CANCEL,
300
this.handleCancel_)
301
.listen(
302
dialog, goog.ui.editor.LinkDialog.EventType.BEFORE_TEST_LINK,
303
this.handleBeforeTestLink);
304
return dialog;
305
};
306
307
308
/** @override */
309
goog.editor.plugins.LinkDialogPlugin.prototype.disposeInternal = function() {
310
goog.editor.plugins.LinkDialogPlugin.base(this, 'disposeInternal');
311
this.eventHandler_.dispose();
312
};
313
314
315
/**
316
* Handles the OK event from the dialog by updating the link in the field.
317
* @param {goog.ui.editor.LinkDialog.OkEvent} e OK event object.
318
* @protected
319
*/
320
goog.editor.plugins.LinkDialogPlugin.prototype.handleOk = function(e) {
321
// We're not restoring the original selection, so clear it out.
322
this.disposeOriginalSelection();
323
324
this.currentLink_.setTextAndUrl(e.linkText, e.linkUrl);
325
if (this.showOpenLinkInNewWindow_) {
326
// Save checkbox state for next time.
327
this.isOpenLinkInNewWindowChecked_ = e.openInNewWindow;
328
}
329
330
var anchor = this.currentLink_.getAnchor();
331
this.touchUpAnchorOnOk_(anchor, e);
332
var extraAnchors = this.currentLink_.getExtraAnchors();
333
for (var i = 0; i < extraAnchors.length; ++i) {
334
extraAnchors[i].href = anchor.href;
335
this.touchUpAnchorOnOk_(extraAnchors[i], e);
336
}
337
338
// Place cursor to the right of the modified link.
339
this.currentLink_.placeCursorRightOf();
340
341
this.getFieldObject().focus();
342
343
this.getFieldObject().dispatchSelectionChangeEvent();
344
this.getFieldObject().dispatchChange();
345
346
this.eventHandler_.removeAll();
347
};
348
349
350
/**
351
* Apply the necessary properties to a link upon Ok being clicked in the dialog.
352
* @param {HTMLAnchorElement} anchor The anchor to set properties on.
353
* @param {goog.events.Event} e Event object.
354
* @private
355
*/
356
goog.editor.plugins.LinkDialogPlugin.prototype.touchUpAnchorOnOk_ = function(
357
anchor, e) {
358
if (this.showOpenLinkInNewWindow_) {
359
if (e.openInNewWindow) {
360
anchor.target = '_blank';
361
} else {
362
if (anchor.target == '_blank') {
363
anchor.target = '';
364
}
365
// If user didn't indicate to open in a new window but the link already
366
// had a target other than '_blank', let's leave what they had before.
367
}
368
}
369
370
if (this.showRelNoFollow_) {
371
var alreadyPresent = goog.ui.editor.LinkDialog.hasNoFollow(anchor.rel);
372
if (alreadyPresent && !e.noFollow) {
373
anchor.rel = goog.ui.editor.LinkDialog.removeNoFollow(anchor.rel);
374
} else if (!alreadyPresent && e.noFollow) {
375
anchor.rel = anchor.rel ? anchor.rel + ' nofollow' : 'nofollow';
376
}
377
}
378
};
379
380
381
/**
382
* Handles the CANCEL event from the dialog by clearing the anchor if needed.
383
* @param {goog.events.Event} e Event object.
384
* @private
385
*/
386
goog.editor.plugins.LinkDialogPlugin.prototype.handleCancel_ = function(e) {
387
if (this.currentLink_.isNew()) {
388
goog.dom.flattenElement(this.currentLink_.getAnchor());
389
var extraAnchors = this.currentLink_.getExtraAnchors();
390
for (var i = 0; i < extraAnchors.length; ++i) {
391
goog.dom.flattenElement(extraAnchors[i]);
392
}
393
// Make sure listeners know the anchor was flattened out.
394
this.getFieldObject().dispatchChange();
395
}
396
397
this.eventHandler_.removeAll();
398
};
399
400
401
/**
402
* Handles the BeforeTestLink event fired when the 'test' link is clicked.
403
* @param {goog.ui.editor.LinkDialog.BeforeTestLinkEvent} e BeforeTestLink event
404
* object.
405
* @protected
406
*/
407
goog.editor.plugins.LinkDialogPlugin.prototype.handleBeforeTestLink = function(
408
e) {
409
if (!this.shouldOpenUrl(e.url)) {
410
/** @desc Message when the user tries to test (preview) a link, but the
411
* link cannot be tested. */
412
var MSG_UNSAFE_LINK = goog.getMsg('This link cannot be tested.');
413
alert(MSG_UNSAFE_LINK);
414
e.preventDefault();
415
}
416
};
417
418
419
/**
420
* Checks whether the plugin should open the given url in a new window.
421
* @param {string} url The url to check.
422
* @return {boolean} If the plugin should open the given url in a new window.
423
* @protected
424
*/
425
goog.editor.plugins.LinkDialogPlugin.prototype.shouldOpenUrl = function(url) {
426
return !this.blockOpeningUnsafeSchemes_ || this.isSafeSchemeToOpen_(url);
427
};
428
429
430
/**
431
* Determines whether or not a url has a scheme which is safe to open.
432
* Schemes like javascript are unsafe due to the possibility of XSS.
433
* @param {string} url A url.
434
* @return {boolean} Whether the url has a safe scheme.
435
* @private
436
*/
437
goog.editor.plugins.LinkDialogPlugin.prototype.isSafeSchemeToOpen_ = function(
438
url) {
439
var scheme = goog.uri.utils.getScheme(url) || 'http';
440
return goog.array.contains(this.safeToOpenSchemes_, scheme.toLowerCase());
441
};
442
443