Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/editor/plugins/abstractbubbleplugin.js
1865 views
1
// Copyright 2005 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 Base class for bubble plugins.
17
* @author [email protected] (Robby Walker)
18
*/
19
20
goog.provide('goog.editor.plugins.AbstractBubblePlugin');
21
22
goog.require('goog.array');
23
goog.require('goog.dom');
24
goog.require('goog.dom.NodeType');
25
goog.require('goog.dom.Range');
26
goog.require('goog.dom.TagName');
27
goog.require('goog.dom.classlist');
28
goog.require('goog.editor.Plugin');
29
goog.require('goog.editor.style');
30
goog.require('goog.events');
31
goog.require('goog.events.EventHandler');
32
goog.require('goog.events.EventType');
33
goog.require('goog.events.KeyCodes');
34
goog.require('goog.events.actionEventWrapper');
35
goog.require('goog.functions');
36
goog.require('goog.string.Unicode');
37
goog.require('goog.ui.Component');
38
goog.require('goog.ui.editor.Bubble');
39
goog.require('goog.userAgent');
40
41
42
43
/**
44
* Base class for bubble plugins. This is used for to connect user behavior
45
* in the editor to a goog.ui.editor.Bubble UI element that allows
46
* the user to modify the properties of an element on their page (e.g. the alt
47
* text of an image tag).
48
*
49
* Subclasses should override the abstract method getBubbleTargetFromSelection()
50
* with code to determine if the current selection should activate the bubble
51
* type. The other abstract method createBubbleContents() should be overriden
52
* with code to create the inside markup of the bubble. The base class creates
53
* the rest of the bubble.
54
*
55
* @constructor
56
* @extends {goog.editor.Plugin}
57
*/
58
goog.editor.plugins.AbstractBubblePlugin = function() {
59
goog.editor.plugins.AbstractBubblePlugin.base(this, 'constructor');
60
61
/**
62
* Place to register events the plugin listens to.
63
* @type {goog.events.EventHandler<
64
* !goog.editor.plugins.AbstractBubblePlugin>}
65
* @protected
66
*/
67
this.eventRegister = new goog.events.EventHandler(this);
68
69
/**
70
* Instance factory function that creates a bubble UI component. If set to a
71
* non-null value, this function will be used to create a bubble instead of
72
* the global factory function. It takes as parameters the bubble parent
73
* element and the z index to draw the bubble at.
74
* @type {?function(!Element, number): !goog.ui.editor.Bubble}
75
* @private
76
*/
77
this.bubbleFactory_ = null;
78
};
79
goog.inherits(goog.editor.plugins.AbstractBubblePlugin, goog.editor.Plugin);
80
81
82
/**
83
* The css class name of option link elements.
84
* @type {string}
85
* @private
86
*/
87
goog.editor.plugins.AbstractBubblePlugin.OPTION_LINK_CLASSNAME_ =
88
goog.getCssName('tr_option-link');
89
90
91
/**
92
* The css class name of link elements.
93
* @type {string}
94
* @private
95
*/
96
goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_ =
97
goog.getCssName('tr_bubble_link');
98
99
100
/**
101
* A class name to mark elements that should be reachable by keyboard tabbing.
102
* @type {string}
103
* @private
104
*/
105
goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_ =
106
goog.getCssName('tr_bubble_tabbable');
107
108
109
/**
110
* The constant string used to separate option links.
111
* @type {string}
112
* @protected
113
*/
114
goog.editor.plugins.AbstractBubblePlugin.DASH_NBSP_STRING =
115
goog.string.Unicode.NBSP + '-' + goog.string.Unicode.NBSP;
116
117
118
/**
119
* Default factory function for creating a bubble UI component.
120
* @param {!Element} parent The parent element for the bubble.
121
* @param {number} zIndex The z index to draw the bubble at.
122
* @return {!goog.ui.editor.Bubble} The new bubble component.
123
* @private
124
*/
125
goog.editor.plugins.AbstractBubblePlugin.defaultBubbleFactory_ = function(
126
parent, zIndex) {
127
return new goog.ui.editor.Bubble(parent, zIndex);
128
};
129
130
131
/**
132
* Global factory function that creates a bubble UI component. It takes as
133
* parameters the bubble parent element and the z index to draw the bubble at.
134
* @type {function(!Element, number): !goog.ui.editor.Bubble}
135
* @private
136
*/
137
goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_ =
138
goog.editor.plugins.AbstractBubblePlugin.defaultBubbleFactory_;
139
140
141
/**
142
* Sets the global bubble factory function.
143
* @param {function(!Element, number): !goog.ui.editor.Bubble}
144
* bubbleFactory Function that creates a bubble for the given bubble parent
145
* element and z index.
146
*/
147
goog.editor.plugins.AbstractBubblePlugin.setBubbleFactory = function(
148
bubbleFactory) {
149
goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_ = bubbleFactory;
150
};
151
152
153
/**
154
* Map from field id to shared bubble object.
155
* @type {!Object<goog.ui.editor.Bubble>}
156
* @private
157
*/
158
goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {};
159
160
161
/**
162
* The optional parent of the bubble. If null or not set, we will use the
163
* application document. This is useful when you have an editor embedded in
164
* a scrolling DIV.
165
* @type {Element|undefined}
166
* @private
167
*/
168
goog.editor.plugins.AbstractBubblePlugin.prototype.bubbleParent_;
169
170
171
/**
172
* The id of the panel this plugin added to the shared bubble. Null when
173
* this plugin doesn't currently have a panel in a bubble.
174
* @type {string?}
175
* @private
176
*/
177
goog.editor.plugins.AbstractBubblePlugin.prototype.panelId_ = null;
178
179
180
/**
181
* Whether this bubble should support tabbing through elements. False
182
* by default.
183
* @type {boolean}
184
* @private
185
*/
186
goog.editor.plugins.AbstractBubblePlugin.prototype.keyboardNavigationEnabled_ =
187
false;
188
189
190
/**
191
* Sets the instance bubble factory function. If set to a non-null value, this
192
* function will be used to create a bubble instead of the global factory
193
* function.
194
* @param {?function(!Element, number): !goog.ui.editor.Bubble} bubbleFactory
195
* Function that creates a bubble for the given bubble parent element and z
196
* index. Null to reset the factory function.
197
*/
198
goog.editor.plugins.AbstractBubblePlugin.prototype.setBubbleFactory = function(
199
bubbleFactory) {
200
this.bubbleFactory_ = bubbleFactory;
201
};
202
203
204
/**
205
* Sets whether the bubble should support tabbing through elements.
206
* @param {boolean} keyboardNavigationEnabled
207
*/
208
goog.editor.plugins.AbstractBubblePlugin.prototype.enableKeyboardNavigation =
209
function(keyboardNavigationEnabled) {
210
this.keyboardNavigationEnabled_ = keyboardNavigationEnabled;
211
};
212
213
214
/**
215
* Sets the bubble parent.
216
* @param {Element} bubbleParent An element where the bubble will be
217
* anchored. If null, we will use the application document. This
218
* is useful when you have an editor embedded in a scrolling div.
219
*/
220
goog.editor.plugins.AbstractBubblePlugin.prototype.setBubbleParent = function(
221
bubbleParent) {
222
this.bubbleParent_ = bubbleParent;
223
};
224
225
226
/**
227
* Returns the bubble map. Subclasses may override to use a separate map.
228
* @return {!Object<goog.ui.editor.Bubble>}
229
* @protected
230
*/
231
goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleMap = function() {
232
return goog.editor.plugins.AbstractBubblePlugin.bubbleMap_;
233
};
234
235
236
/**
237
* @return {goog.dom.DomHelper} The dom helper for the bubble window.
238
*/
239
goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleDom = function() {
240
return this.dom_;
241
};
242
243
244
/** @override */
245
goog.editor.plugins.AbstractBubblePlugin.prototype.getTrogClassId =
246
goog.functions.constant('AbstractBubblePlugin');
247
248
249
/**
250
* Returns the element whose properties the bubble manipulates.
251
* @return {Element} The target element.
252
*/
253
goog.editor.plugins.AbstractBubblePlugin.prototype.getTargetElement =
254
function() {
255
return this.targetElement_;
256
};
257
258
259
/** @override */
260
goog.editor.plugins.AbstractBubblePlugin.prototype.handleKeyUp = function(e) {
261
// For example, when an image is selected, pressing any key overwrites
262
// the image and the panel should be hidden.
263
// Therefore we need to track key presses when the bubble is showing.
264
if (this.isVisible()) {
265
this.handleSelectionChange();
266
}
267
return false;
268
};
269
270
271
/**
272
* Pops up a property bubble for the given selection if appropriate and closes
273
* open property bubbles if no longer needed. This should not be overridden.
274
* @override
275
*/
276
goog.editor.plugins.AbstractBubblePlugin.prototype.handleSelectionChange =
277
function(opt_e, opt_target) {
278
var selectedElement;
279
if (opt_e) {
280
selectedElement = /** @type {Element} */ (opt_e.target);
281
} else if (opt_target) {
282
selectedElement = /** @type {Element} */ (opt_target);
283
} else {
284
var range = this.getFieldObject().getRange();
285
if (range) {
286
var startNode = range.getStartNode();
287
var endNode = range.getEndNode();
288
var startOffset = range.getStartOffset();
289
var endOffset = range.getEndOffset();
290
// Sometimes in IE, the range will be collapsed, but think the end node
291
// and start node are different (although in the same visible position).
292
// In this case, favor the position IE thinks is the start node.
293
if (goog.userAgent.IE && range.isCollapsed() && startNode != endNode) {
294
range = goog.dom.Range.createCaret(startNode, startOffset);
295
}
296
if (startNode.nodeType == goog.dom.NodeType.ELEMENT &&
297
startNode == endNode && startOffset == endOffset - 1) {
298
var element = startNode.childNodes[startOffset];
299
if (element.nodeType == goog.dom.NodeType.ELEMENT) {
300
selectedElement = /** @type {!Element} */ (element);
301
}
302
}
303
}
304
selectedElement = selectedElement || range && range.getContainerElement();
305
}
306
return this.handleSelectionChangeInternal(selectedElement);
307
};
308
309
310
/**
311
* Pops up a property bubble for the given selection if appropriate and closes
312
* open property bubbles if no longer needed.
313
* @param {Element?} selectedElement The selected element.
314
* @return {boolean} Always false, allowing every bubble plugin to handle the
315
* event.
316
* @protected
317
*/
318
goog.editor.plugins.AbstractBubblePlugin.prototype
319
.handleSelectionChangeInternal = function(selectedElement) {
320
if (selectedElement) {
321
var bubbleTarget = this.getBubbleTargetFromSelection(selectedElement);
322
if (bubbleTarget) {
323
if (bubbleTarget != this.targetElement_ || !this.panelId_) {
324
// Make sure any existing panel of the same type is closed before
325
// creating a new one.
326
if (this.panelId_) {
327
this.closeBubble();
328
}
329
this.createBubble(bubbleTarget);
330
}
331
return false;
332
}
333
}
334
335
if (this.panelId_) {
336
this.closeBubble();
337
}
338
339
return false;
340
};
341
342
343
/**
344
* Should be overriden by subclasses to return the bubble target element or
345
* null if an element of their required type isn't found.
346
* @param {Element} selectedElement The target of the selection change event or
347
* the parent container of the current entire selection.
348
* @return {Element?} The HTML bubble target element or null if no element of
349
* the required type is not found.
350
*/
351
goog.editor.plugins.AbstractBubblePlugin.prototype
352
.getBubbleTargetFromSelection = goog.abstractMethod;
353
354
355
/** @override */
356
goog.editor.plugins.AbstractBubblePlugin.prototype.disable = function(field) {
357
// When the field is made uneditable, dispose of the bubble. We do this
358
// because the next time the field is made editable again it may be in
359
// a different document / iframe.
360
if (field.isUneditable()) {
361
var bubbleMap = this.getBubbleMap();
362
var bubble = bubbleMap[field.id];
363
if (bubble) {
364
if (field == this.getFieldObject()) {
365
this.closeBubble();
366
}
367
bubble.dispose();
368
delete bubbleMap[field.id];
369
}
370
}
371
};
372
373
374
/**
375
* @return {!goog.ui.editor.Bubble} The shared bubble object for the field this
376
* plugin is registered on. Creates it if necessary.
377
* @private
378
*/
379
goog.editor.plugins.AbstractBubblePlugin.prototype.getSharedBubble_ =
380
function() {
381
var bubbleParent = /** @type {!Element} */ (
382
this.bubbleParent_ || this.getFieldObject().getAppWindow().document.body);
383
this.dom_ = goog.dom.getDomHelper(bubbleParent);
384
385
var bubbleMap = this.getBubbleMap();
386
var bubble = bubbleMap[this.getFieldObject().id];
387
if (!bubble) {
388
var factory = this.bubbleFactory_ ||
389
goog.editor.plugins.AbstractBubblePlugin.globalBubbleFactory_;
390
bubble =
391
factory.call(null, bubbleParent, this.getFieldObject().getBaseZindex());
392
bubbleMap[this.getFieldObject().id] = bubble;
393
}
394
return bubble;
395
};
396
397
398
/**
399
* Creates and shows the property bubble.
400
* @param {Element} targetElement The target element of the bubble.
401
*/
402
goog.editor.plugins.AbstractBubblePlugin.prototype.createBubble = function(
403
targetElement) {
404
var bubble = this.getSharedBubble_();
405
if (!bubble.hasPanelOfType(this.getBubbleType())) {
406
this.targetElement_ = targetElement;
407
408
this.panelId_ = bubble.addPanel(
409
this.getBubbleType(), this.getBubbleTitle(), targetElement,
410
goog.bind(this.createBubbleContents, this),
411
this.shouldPreferBubbleAboveElement());
412
this.eventRegister.listen(
413
bubble, goog.ui.Component.EventType.HIDE, this.handlePanelClosed_);
414
415
this.onShow();
416
417
if (this.keyboardNavigationEnabled_) {
418
this.eventRegister.listen(
419
bubble.getContentElement(), goog.events.EventType.KEYDOWN,
420
this.onBubbleKey_);
421
}
422
}
423
};
424
425
426
/**
427
* @return {string} The type of bubble shown by this plugin. Usually the tag
428
* name of the element this bubble targets.
429
* @protected
430
*/
431
goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleType = function() {
432
return '';
433
};
434
435
436
/**
437
* @return {string} The title for bubble shown by this plugin. Defaults to no
438
* title. Should be overridden by subclasses.
439
* @protected
440
*/
441
goog.editor.plugins.AbstractBubblePlugin.prototype.getBubbleTitle = function() {
442
return '';
443
};
444
445
446
/**
447
* @return {boolean} Whether the bubble should prefer placement above the
448
* target element.
449
* @protected
450
*/
451
goog.editor.plugins.AbstractBubblePlugin.prototype
452
.shouldPreferBubbleAboveElement = goog.functions.FALSE;
453
454
455
/**
456
* Should be overriden by subclasses to add the type specific contents to the
457
* bubble.
458
* @param {Element} bubbleContainer The container element of the bubble to
459
* which the contents should be added.
460
* @protected
461
*/
462
goog.editor.plugins.AbstractBubblePlugin.prototype.createBubbleContents =
463
goog.abstractMethod;
464
465
466
/**
467
* Register the handler for the target's CLICK event.
468
* @param {Element} target The event source element.
469
* @param {Function} handler The event handler.
470
* @protected
471
* @deprecated Use goog.editor.plugins.AbstractBubblePlugin.
472
* registerActionHandler to register click and enter events.
473
*/
474
goog.editor.plugins.AbstractBubblePlugin.prototype.registerClickHandler =
475
function(target, handler) {
476
this.registerActionHandler(target, handler);
477
};
478
479
480
/**
481
* Register the handler for the target's CLICK and ENTER key events.
482
* @param {Element} target The event source element.
483
* @param {Function} handler The event handler.
484
* @protected
485
*/
486
goog.editor.plugins.AbstractBubblePlugin.prototype.registerActionHandler =
487
function(target, handler) {
488
this.eventRegister.listenWithWrapper(
489
target, goog.events.actionEventWrapper, handler);
490
};
491
492
493
/**
494
* Closes the bubble.
495
*/
496
goog.editor.plugins.AbstractBubblePlugin.prototype.closeBubble = function() {
497
if (this.panelId_) {
498
this.getSharedBubble_().removePanel(this.panelId_);
499
this.handlePanelClosed_();
500
}
501
};
502
503
504
/**
505
* Called after the bubble is shown. The default implementation does nothing.
506
* Override it to provide your own one.
507
* @protected
508
*/
509
goog.editor.plugins.AbstractBubblePlugin.prototype.onShow = goog.nullFunction;
510
511
512
/**
513
* Called when the bubble is closed or hidden. The default implementation does
514
* nothing.
515
* @protected
516
*/
517
goog.editor.plugins.AbstractBubblePlugin.prototype.cleanOnBubbleClose =
518
goog.nullFunction;
519
520
521
/**
522
* Handles when the bubble panel is closed. Invoked when the entire bubble is
523
* hidden and also directly when the panel is closed manually.
524
* @private
525
*/
526
goog.editor.plugins.AbstractBubblePlugin.prototype.handlePanelClosed_ =
527
function() {
528
this.targetElement_ = null;
529
this.panelId_ = null;
530
this.eventRegister.removeAll();
531
this.cleanOnBubbleClose();
532
};
533
534
535
/**
536
* In case the keyboard navigation is enabled, this will set focus on the first
537
* tabbable element in the bubble when TAB is clicked.
538
* @override
539
*/
540
goog.editor.plugins.AbstractBubblePlugin.prototype.handleKeyDown = function(e) {
541
if (this.keyboardNavigationEnabled_ && this.isVisible() &&
542
e.keyCode == goog.events.KeyCodes.TAB && !e.shiftKey) {
543
var bubbleEl = this.getSharedBubble_().getContentElement();
544
var tabbable = goog.dom.getElementByClass(
545
goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_, bubbleEl);
546
if (tabbable) {
547
tabbable.focus();
548
e.preventDefault();
549
return true;
550
}
551
}
552
return false;
553
};
554
555
556
/**
557
* Handles a key event on the bubble. This ensures that the focus loops through
558
* the tabbable elements found in the bubble and then the focus is got by the
559
* field element.
560
* @param {goog.events.BrowserEvent} e The event.
561
* @private
562
*/
563
goog.editor.plugins.AbstractBubblePlugin.prototype.onBubbleKey_ = function(e) {
564
if (this.isVisible() && e.keyCode == goog.events.KeyCodes.TAB) {
565
var bubbleEl = this.getSharedBubble_().getContentElement();
566
var tabbables = goog.dom.getElementsByClass(
567
goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_, bubbleEl);
568
var tabbable = e.shiftKey ? tabbables[0] : goog.array.peek(tabbables);
569
var tabbingOutOfBubble = tabbable == e.target;
570
if (tabbingOutOfBubble) {
571
this.getFieldObject().focus();
572
e.preventDefault();
573
}
574
}
575
};
576
577
578
/**
579
* @return {boolean} Whether the bubble is visible.
580
*/
581
goog.editor.plugins.AbstractBubblePlugin.prototype.isVisible = function() {
582
return !!this.panelId_;
583
};
584
585
586
/**
587
* Reposition the property bubble.
588
*/
589
goog.editor.plugins.AbstractBubblePlugin.prototype.reposition = function() {
590
var bubble = this.getSharedBubble_();
591
if (bubble) {
592
bubble.reposition();
593
}
594
};
595
596
597
/**
598
* Helper method that creates option links (such as edit, test, remove)
599
* @param {string} id String id for the span id.
600
* @return {Element} The option link element.
601
* @protected
602
*/
603
goog.editor.plugins.AbstractBubblePlugin.prototype.createLinkOption = function(
604
id) {
605
// Dash plus link are together in a span so we can hide/show them easily
606
return this.dom_.createDom(
607
goog.dom.TagName.SPAN, {
608
id: id,
609
className:
610
goog.editor.plugins.AbstractBubblePlugin.OPTION_LINK_CLASSNAME_
611
},
612
this.dom_.createTextNode(
613
goog.editor.plugins.AbstractBubblePlugin.DASH_NBSP_STRING));
614
};
615
616
617
/**
618
* Helper method that creates a link with text set to linkText and optionally
619
* wires up a listener for the CLICK event or the link. The link is navigable by
620
* tabs if {@code enableKeyboardNavigation(true)} was called.
621
* @param {string} linkId The id of the link.
622
* @param {string} linkText Text of the link.
623
* @param {Function=} opt_onClick Optional function to call when the link is
624
* clicked.
625
* @param {Element=} opt_container If specified, location to insert link. If no
626
* container is specified, the old link is removed and replaced.
627
* @return {Element} The link element.
628
* @protected
629
*/
630
goog.editor.plugins.AbstractBubblePlugin.prototype.createLink = function(
631
linkId, linkText, opt_onClick, opt_container) {
632
var link = this.createLinkHelper(linkId, linkText, false, opt_container);
633
if (opt_onClick) {
634
this.registerActionHandler(link, opt_onClick);
635
}
636
return link;
637
};
638
639
640
/**
641
* Helper method to create a link to insert into the bubble. The link is
642
* navigable by tabs if {@code enableKeyboardNavigation(true)} was called.
643
* @param {string} linkId The id of the link.
644
* @param {string} linkText Text of the link.
645
* @param {boolean} isAnchor Set to true to create an actual anchor tag
646
* instead of a span. Actual links are right clickable (e.g. to open in
647
* a new window) and also update window status on hover.
648
* @param {Element=} opt_container If specified, location to insert link. If no
649
* container is specified, the old link is removed and replaced.
650
* @return {Element} The link element.
651
* @protected
652
*/
653
goog.editor.plugins.AbstractBubblePlugin.prototype.createLinkHelper = function(
654
linkId, linkText, isAnchor, opt_container) {
655
var link = this.dom_.createDom(
656
isAnchor ? goog.dom.TagName.A : goog.dom.TagName.SPAN,
657
{className: goog.editor.plugins.AbstractBubblePlugin.LINK_CLASSNAME_},
658
linkText);
659
if (this.keyboardNavigationEnabled_) {
660
this.setTabbable(link);
661
}
662
link.setAttribute('role', 'link');
663
this.setupLink(link, linkId, opt_container);
664
goog.editor.style.makeUnselectable(link, this.eventRegister);
665
return link;
666
};
667
668
669
/**
670
* Makes the given element tabbable.
671
*
672
* <p>Elements created by createLink[Helper] are tabbable even without
673
* calling this method. Call it for other elements if needed.
674
*
675
* <p>If tabindex is not already set in the element, this function sets it to 0.
676
* You'll usually want to also call {@code enableKeyboardNavigation(true)}.
677
*
678
* @param {!Element} element
679
* @protected
680
*/
681
goog.editor.plugins.AbstractBubblePlugin.prototype.setTabbable = function(
682
element) {
683
if (!element.hasAttribute('tabindex')) {
684
element.setAttribute('tabindex', 0);
685
}
686
goog.dom.classlist.add(
687
element, goog.editor.plugins.AbstractBubblePlugin.TABBABLE_CLASSNAME_);
688
};
689
690
691
/**
692
* Inserts a link in the given container if it is specified or removes
693
* the old link with this id and replaces it with the new link
694
* @param {Element} link Html element to insert.
695
* @param {string} linkId Id of the link.
696
* @param {Element=} opt_container If specified, location to insert link.
697
* @protected
698
*/
699
goog.editor.plugins.AbstractBubblePlugin.prototype.setupLink = function(
700
link, linkId, opt_container) {
701
if (opt_container) {
702
opt_container.appendChild(link);
703
} else {
704
var oldLink = this.dom_.getElement(linkId);
705
if (oldLink) {
706
goog.dom.replaceNode(link, oldLink);
707
}
708
}
709
710
link.id = linkId;
711
};
712
713