Adding float-no-zero branch hosted build
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Wrapper around {@link goog.ui.Dialog}, to provide
|
||||
* dialogs that are smarter about interacting with a rich text editor.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.AbstractDialog');
|
||||
goog.provide('goog.ui.editor.AbstractDialog.Builder');
|
||||
goog.provide('goog.ui.editor.AbstractDialog.EventType');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.Dialog');
|
||||
|
||||
|
||||
// *** Public interface ***************************************************** //
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an object that represents a dialog box.
|
||||
* @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
|
||||
* dialog's dom structure.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog = function(domHelper) {
|
||||
goog.events.EventTarget.call(this);
|
||||
this.dom = domHelper;
|
||||
};
|
||||
goog.inherits(goog.ui.editor.AbstractDialog, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Causes the dialog box to appear, centered on the screen. Lazily creates the
|
||||
* dialog if needed.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.show = function() {
|
||||
// Lazily create the wrapped dialog to be shown.
|
||||
if (!this.dialogInternal_) {
|
||||
this.dialogInternal_ = this.createDialogControl();
|
||||
this.dialogInternal_.addEventListener(goog.ui.Dialog.EventType.AFTER_HIDE,
|
||||
this.handleAfterHide_, false, this);
|
||||
}
|
||||
|
||||
this.dialogInternal_.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hides the dialog, causing AFTER_HIDE to fire.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.hide = function() {
|
||||
if (this.dialogInternal_) {
|
||||
// This eventually fires the wrapped dialog's AFTER_HIDE event, calling our
|
||||
// handleAfterHide_().
|
||||
this.dialogInternal_.setVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the dialog is open.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.isOpen = function() {
|
||||
return !!this.dialogInternal_ && this.dialogInternal_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs the handler registered on the OK button event and closes the dialog if
|
||||
* that handler succeeds.
|
||||
* This is useful in cases such as double-clicking an item in the dialog is
|
||||
* equivalent to selecting it and clicking the default button.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.processOkAndClose = function() {
|
||||
// Fake an OK event from the wrapped dialog control.
|
||||
var evt = new goog.ui.Dialog.Event(goog.ui.Dialog.DefaultButtonKeys.OK, null);
|
||||
if (this.handleOk(evt)) {
|
||||
// handleOk calls dispatchEvent, so if any listener calls preventDefault it
|
||||
// will return false and we won't hide the dialog.
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// *** Dialog events ******************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* Event type constants for events the dialog fires.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.EventType = {
|
||||
// This event is fired after the dialog is hidden, no matter if it was closed
|
||||
// via OK or Cancel or is being disposed without being hidden first.
|
||||
AFTER_HIDE: 'afterhide',
|
||||
// Either the cancel or OK events can be canceled via preventDefault or by
|
||||
// returning false from their handlers to stop the dialog from closing.
|
||||
CANCEL: 'cancel',
|
||||
OK: 'ok'
|
||||
};
|
||||
|
||||
|
||||
// *** Inner helper class *************************************************** //
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A builder class for the dialog control. All methods except build return this.
|
||||
* @param {goog.ui.editor.AbstractDialog} editorDialog Editor dialog object
|
||||
* that will wrap the wrapped dialog object this builder will create.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder = function(editorDialog) {
|
||||
// We require the editor dialog to be passed in so that the builder can set up
|
||||
// ok/cancel listeners by default, making it easier for most dialogs.
|
||||
this.editorDialog_ = editorDialog;
|
||||
this.wrappedDialog_ = new goog.ui.Dialog('', true, this.editorDialog_.dom);
|
||||
this.buttonSet_ = new goog.ui.Dialog.ButtonSet(this.editorDialog_.dom);
|
||||
this.buttonHandlers_ = {};
|
||||
this.addClassName(goog.getCssName('tr-dialog'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the title of the dialog.
|
||||
* @param {string} title Title HTML (escaped).
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.setTitle = function(title) {
|
||||
this.wrappedDialog_.setTitle(title);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an OK button to the dialog. Clicking this button will cause {@link
|
||||
* handleOk} to run, subsequently dispatching an OK event.
|
||||
* @param {string=} opt_label The caption for the button, if not "OK".
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addOkButton =
|
||||
function(opt_label) {
|
||||
var key = goog.ui.Dialog.DefaultButtonKeys.OK;
|
||||
/** @desc Label for an OK button in an editor dialog. */
|
||||
var MSG_TR_DIALOG_OK = goog.getMsg('OK');
|
||||
// True means this is the default/OK button.
|
||||
this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_OK, true);
|
||||
this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleOk,
|
||||
this.editorDialog_);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a Cancel button to the dialog. Clicking this button will cause {@link
|
||||
* handleCancel} to run, subsequently dispatching a CANCEL event.
|
||||
* @param {string=} opt_label The caption for the button, if not "Cancel".
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addCancelButton =
|
||||
function(opt_label) {
|
||||
var key = goog.ui.Dialog.DefaultButtonKeys.CANCEL;
|
||||
/** @desc Label for a cancel button in an editor dialog. */
|
||||
var MSG_TR_DIALOG_CANCEL = goog.getMsg('Cancel');
|
||||
// False means it's not the OK button, true means it's the Cancel button.
|
||||
this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_CANCEL, false, true);
|
||||
this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleCancel,
|
||||
this.editorDialog_);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom button to the dialog.
|
||||
* @param {string} label The caption for the button.
|
||||
* @param {function(goog.ui.Dialog.EventType):*} handler Function called when
|
||||
* the button is clicked. It is recommended that this function be a method
|
||||
* in the concrete subclass of AbstractDialog using this Builder, and that
|
||||
* it dispatch an event (see {@link handleOk}).
|
||||
* @param {string=} opt_buttonId Identifier to be used to access the button when
|
||||
* calling AbstractDialog.getButtonElement().
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addButton =
|
||||
function(label, handler, opt_buttonId) {
|
||||
// We don't care what the key is, just that we can match the button with the
|
||||
// handler function later.
|
||||
var key = opt_buttonId || goog.string.createUniqueString();
|
||||
this.buttonSet_.set(key, label);
|
||||
this.buttonHandlers_[key] = handler;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Puts a CSS class on the dialog's main element.
|
||||
* @param {string} className The class to add.
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addClassName =
|
||||
function(className) {
|
||||
goog.dom.classes.add(this.wrappedDialog_.getDialogElement(), className);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the content element of the dialog.
|
||||
* @param {Element} contentElem An element for the main body.
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.setContent =
|
||||
function(contentElem) {
|
||||
goog.dom.appendChild(this.wrappedDialog_.getContentElement(), contentElem);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds the wrapped dialog control. May only be called once, after which
|
||||
* no more methods may be called on this builder.
|
||||
* @return {goog.ui.Dialog} The wrapped dialog control.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.build = function() {
|
||||
if (this.buttonSet_.isEmpty()) {
|
||||
// If caller didn't set any buttons, add an OK and Cancel button by default.
|
||||
this.addOkButton();
|
||||
this.addCancelButton();
|
||||
}
|
||||
this.wrappedDialog_.setButtonSet(this.buttonSet_);
|
||||
|
||||
var handlers = this.buttonHandlers_;
|
||||
this.buttonHandlers_ = null;
|
||||
this.wrappedDialog_.addEventListener(goog.ui.Dialog.EventType.SELECT,
|
||||
// Listen for the SELECT event, which means a button was clicked, and
|
||||
// call the handler associated with that button via the key property.
|
||||
function(e) {
|
||||
if (handlers[e.key]) {
|
||||
return handlers[e.key](e);
|
||||
}
|
||||
});
|
||||
|
||||
// All editor dialogs are modal.
|
||||
this.wrappedDialog_.setModal(true);
|
||||
|
||||
var dialog = this.wrappedDialog_;
|
||||
this.wrappedDialog_ = null;
|
||||
return dialog;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Editor dialog that will wrap the wrapped dialog this builder will create.
|
||||
* @type {goog.ui.editor.AbstractDialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.editorDialog_;
|
||||
|
||||
|
||||
/**
|
||||
* wrapped dialog control being built by this builder.
|
||||
* @type {goog.ui.Dialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.wrappedDialog_;
|
||||
|
||||
|
||||
/**
|
||||
* Set of buttons to be added to the wrapped dialog control.
|
||||
* @type {goog.ui.Dialog.ButtonSet}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.buttonSet_;
|
||||
|
||||
|
||||
/**
|
||||
* Map from keys that will be returned in the wrapped dialog SELECT events to
|
||||
* handler functions to be called to handle those events.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.buttonHandlers_;
|
||||
|
||||
|
||||
// *** Protected interface ************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* The DOM helper for the parent document.
|
||||
* @type {goog.dom.DomHelper}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.dom;
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns the goog.ui.Dialog control that is being wrapped
|
||||
* by this object.
|
||||
* @return {goog.ui.Dialog} Created Dialog control.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.createDialogControl =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the OK button in this dialog.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getOkButtonElement = function() {
|
||||
return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.OK);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the Cancel button in this dialog.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getCancelButtonElement = function() {
|
||||
return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.CANCEL);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the button added to this dialog with
|
||||
* the given button id.
|
||||
* @param {string} buttonId The id of the button to get.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getButtonElement = function(buttonId) {
|
||||
return this.dialogInternal_.getButtonSet().getButton(buttonId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns the event object to be used when dispatching the OK
|
||||
* event to listeners, or returns null to prevent the dialog from closing.
|
||||
* Subclasses should override this to return their own subclass of
|
||||
* goog.events.Event that includes all data a plugin would need from the dialog.
|
||||
* @param {goog.events.Event} e The event object dispatched by the wrapped
|
||||
* dialog.
|
||||
* @return {goog.events.Event} The event object to be used when dispatching the
|
||||
* OK event to listeners.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.createOkEvent = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handles the event dispatched by the wrapped dialog control when the user
|
||||
* clicks the OK button. Attempts to create the OK event object and dispatches
|
||||
* it if successful.
|
||||
* @param {goog.ui.Dialog.Event} e wrapped dialog OK event object.
|
||||
* @return {boolean} Whether the default action (closing the dialog) should
|
||||
* still be executed. This will be false if the OK event could not be
|
||||
* created to be dispatched, or if any listener to that event returs false
|
||||
* or calls preventDefault.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleOk = function(e) {
|
||||
var eventObj = this.createOkEvent(e);
|
||||
if (eventObj) {
|
||||
return this.dispatchEvent(eventObj);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the event dispatched by the wrapped dialog control when the user
|
||||
* clicks the Cancel button. Simply dispatches a CANCEL event.
|
||||
* @return {boolean} Returns false if any of the handlers called prefentDefault
|
||||
* on the event or returned false themselves.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleCancel = function() {
|
||||
return this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disposes of the dialog. If the dialog is open, it will be hidden and
|
||||
* AFTER_HIDE will be dispatched.
|
||||
* @override
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.disposeInternal = function() {
|
||||
if (this.dialogInternal_) {
|
||||
this.hide();
|
||||
|
||||
this.dialogInternal_.dispose();
|
||||
this.dialogInternal_ = null;
|
||||
}
|
||||
|
||||
goog.ui.editor.AbstractDialog.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
// *** Private implementation *********************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* The wrapped dialog widget.
|
||||
* @type {goog.ui.Dialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.dialogInternal_;
|
||||
|
||||
|
||||
/**
|
||||
* Cleans up after the dialog is hidden and fires the AFTER_HIDE event. Should
|
||||
* be a listener for the wrapped dialog's AFTER_HIDE event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleAfterHide_ = function() {
|
||||
this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE);
|
||||
};
|
||||
@@ -0,0 +1,525 @@
|
||||
// Copyright 2005 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Bubble component - handles display, hiding, etc. of the
|
||||
* actual bubble UI.
|
||||
*
|
||||
* This is used exclusively by code within the editor package, and should not
|
||||
* be used directly.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author tildahl@google.com (Michael Tildahl)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.Bubble');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.ViewportSizeMonitor');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.editor.style');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.math.Box');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.positioning');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.positioning.Overflow');
|
||||
goog.require('goog.positioning.OverflowStatus');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.PopupBase');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Property bubble UI element.
|
||||
* @param {Element} parent The parent element for this bubble.
|
||||
* @param {number} zIndex The z index to draw the bubble at.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.Bubble = function(parent, zIndex) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Dom helper for the document the bubble should be shown in.
|
||||
* @type {!goog.dom.DomHelper}
|
||||
* @private
|
||||
*/
|
||||
this.dom_ = goog.dom.getDomHelper(parent);
|
||||
|
||||
/**
|
||||
* Event handler for this bubble.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* Object that monitors the application window for size changes.
|
||||
* @type {goog.dom.ViewportSizeMonitor}
|
||||
* @private
|
||||
*/
|
||||
this.viewPortSizeMonitor_ = new goog.dom.ViewportSizeMonitor(
|
||||
this.dom_.getWindow());
|
||||
|
||||
/**
|
||||
* Maps panel ids to panels.
|
||||
* @type {Object.<goog.ui.editor.Bubble.Panel_>}
|
||||
* @private
|
||||
*/
|
||||
this.panels_ = {};
|
||||
|
||||
/**
|
||||
* Container element for the entire bubble. This may contain elements related
|
||||
* to look and feel or styling of the bubble.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.bubbleContainer_ =
|
||||
this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{'className': goog.ui.editor.Bubble.BUBBLE_CLASSNAME});
|
||||
|
||||
goog.style.setElementShown(this.bubbleContainer_, false);
|
||||
goog.dom.appendChild(parent, this.bubbleContainer_);
|
||||
goog.style.setStyle(this.bubbleContainer_, 'zIndex', zIndex);
|
||||
|
||||
/**
|
||||
* Container element for the bubble panels - this should be some inner element
|
||||
* within (or equal to) bubbleContainer.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.bubbleContents_ = this.createBubbleDom(this.dom_, this.bubbleContainer_);
|
||||
|
||||
/**
|
||||
* Element showing the close box.
|
||||
* @type {!Element}
|
||||
* @private
|
||||
*/
|
||||
this.closeBox_ = this.dom_.createDom(goog.dom.TagName.DIV, {
|
||||
'className': goog.getCssName('tr_bubble_closebox'),
|
||||
'innerHTML': ' '
|
||||
});
|
||||
this.bubbleContents_.appendChild(this.closeBox_);
|
||||
|
||||
// We make bubbles unselectable so that clicking on them does not steal focus
|
||||
// or move the cursor away from the element the bubble is attached to.
|
||||
goog.editor.style.makeUnselectable(this.bubbleContainer_, this.eventHandler_);
|
||||
|
||||
/**
|
||||
* Popup that controls showing and hiding the bubble at the appropriate
|
||||
* position.
|
||||
* @type {goog.ui.PopupBase}
|
||||
* @private
|
||||
*/
|
||||
this.popup_ = new goog.ui.PopupBase(this.bubbleContainer_);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.Bubble, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The css class name of the bubble container element.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.editor.Bubble.BUBBLE_CLASSNAME = goog.getCssName('tr_bubble');
|
||||
|
||||
|
||||
/**
|
||||
* Creates and adds DOM for the bubble UI to the given container. This default
|
||||
* implementation just returns the container itself.
|
||||
* @param {!goog.dom.DomHelper} dom DOM helper to use.
|
||||
* @param {!Element} container Element to add the new elements to.
|
||||
* @return {!Element} The element where bubble content should be added.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.createBubbleDom = function(dom, container) {
|
||||
return container;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A logger for goog.ui.editor.Bubble.
|
||||
* @type {goog.log.Logger}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.logger =
|
||||
goog.log.getLogger('goog.ui.editor.Bubble');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.Bubble.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
|
||||
goog.dom.removeNode(this.bubbleContainer_);
|
||||
this.bubbleContainer_ = null;
|
||||
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
|
||||
this.viewPortSizeMonitor_.dispose();
|
||||
this.viewPortSizeMonitor_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element that where the bubble's contents go.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getContentElement = function() {
|
||||
return this.bubbleContents_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element that contains the bubble.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getContainerElement = function() {
|
||||
return this.bubbleContainer_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.events.EventHandler} The event handler.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getEventHandler = function() {
|
||||
return this.eventHandler_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles user resizing of window.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.handleWindowResize_ = function() {
|
||||
if (this.isVisible()) {
|
||||
this.reposition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether there is already a panel of the given type.
|
||||
* @param {string} type Type of panel to check.
|
||||
* @return {boolean} Whether there is already a panel of the given type.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.hasPanelOfType = function(type) {
|
||||
return goog.object.some(this.panels_, function(panel) {
|
||||
return panel.type == type;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a panel to the bubble.
|
||||
* @param {string} type The type of bubble panel this is. Should usually be
|
||||
* the same as the tagName of the targetElement. This ensures multiple
|
||||
* bubble panels don't appear for the same element.
|
||||
* @param {string} title The title of the panel.
|
||||
* @param {Element} targetElement The target element of the bubble.
|
||||
* @param {function(Element): void} contentFn Function that when called with
|
||||
* a container element, will add relevant panel content to it.
|
||||
* @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble
|
||||
* above the element instead of below it. Defaults to preferring below.
|
||||
* If any panel prefers the top position, the top position is used.
|
||||
* @return {string} The id of the panel.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.addPanel = function(type, title, targetElement,
|
||||
contentFn, opt_preferTopPosition) {
|
||||
var id = goog.string.createUniqueString();
|
||||
var panel = new goog.ui.editor.Bubble.Panel_(this.dom_, id, type, title,
|
||||
targetElement, !opt_preferTopPosition);
|
||||
this.panels_[id] = panel;
|
||||
|
||||
// Insert the panel in string order of type. Technically we could use binary
|
||||
// search here but n is really small (probably 0 - 2) so it's not worth it.
|
||||
// The last child of bubbleContents_ is the close box so we take care not
|
||||
// to treat it as a panel element, and we also ensure it stays as the last
|
||||
// element. The intention here is not to create any artificial order, but
|
||||
// just to ensure that it is always consistent.
|
||||
var nextElement;
|
||||
for (var i = 0, len = this.bubbleContents_.childNodes.length - 1; i < len;
|
||||
i++) {
|
||||
var otherChild = this.bubbleContents_.childNodes[i];
|
||||
var otherPanel = this.panels_[otherChild.id];
|
||||
if (otherPanel.type > type) {
|
||||
nextElement = otherChild;
|
||||
break;
|
||||
}
|
||||
}
|
||||
goog.dom.insertSiblingBefore(panel.element,
|
||||
nextElement || this.bubbleContents_.lastChild);
|
||||
|
||||
contentFn(panel.getContentElement());
|
||||
goog.editor.style.makeUnselectable(panel.element, this.eventHandler_);
|
||||
|
||||
var numPanels = goog.object.getCount(this.panels_);
|
||||
if (numPanels == 1) {
|
||||
this.openBubble_();
|
||||
} else if (numPanels == 2) {
|
||||
goog.dom.classes.add(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
}
|
||||
this.reposition();
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the panel with the given id.
|
||||
* @param {string} id The id of the panel.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.removePanel = function(id) {
|
||||
var panel = this.panels_[id];
|
||||
goog.dom.removeNode(panel.element);
|
||||
delete this.panels_[id];
|
||||
|
||||
var numPanels = goog.object.getCount(this.panels_);
|
||||
if (numPanels <= 1) {
|
||||
goog.dom.classes.remove(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
}
|
||||
|
||||
if (numPanels == 0) {
|
||||
this.closeBubble_();
|
||||
} else {
|
||||
this.reposition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Opens the bubble.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.openBubble_ = function() {
|
||||
this.eventHandler_.
|
||||
listen(this.closeBox_, goog.events.EventType.CLICK,
|
||||
this.closeBubble_).
|
||||
listen(this.viewPortSizeMonitor_,
|
||||
goog.events.EventType.RESIZE, this.handleWindowResize_).
|
||||
listen(this.popup_, goog.ui.PopupBase.EventType.HIDE,
|
||||
this.handlePopupHide);
|
||||
|
||||
this.popup_.setVisible(true);
|
||||
this.reposition();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Closes the bubble.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.closeBubble_ = function() {
|
||||
this.popup_.setVisible(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the popup's hide event by removing all panels and dispatching a
|
||||
* HIDE event.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.handlePopupHide = function() {
|
||||
// Remove the panel elements.
|
||||
for (var panelId in this.panels_) {
|
||||
goog.dom.removeNode(this.panels_[panelId].element);
|
||||
}
|
||||
|
||||
// Update the state to reflect no panels.
|
||||
this.panels_ = {};
|
||||
goog.dom.classes.remove(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
|
||||
this.eventHandler_.removeAll();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.HIDE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the visibility of the bubble.
|
||||
* @return {boolean} True if visible false if not.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.isVisible = function() {
|
||||
return this.popup_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The vertical clearance in pixels between the bottom of the targetElement
|
||||
* and the edge of the bubble.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_ = goog.userAgent.IE ? 4 : 2;
|
||||
|
||||
|
||||
/**
|
||||
* Bubble's margin box to be passed to goog.positioning.
|
||||
* @type {goog.math.Box}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.MARGIN_BOX_ = new goog.math.Box(
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0,
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0);
|
||||
|
||||
|
||||
/**
|
||||
* Positions and displays this bubble below its targetElement. Assumes that
|
||||
* the bubbleContainer is already contained in the document object it applies
|
||||
* to.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.reposition = function() {
|
||||
var targetElement = null;
|
||||
var preferBottomPosition = true;
|
||||
for (var panelId in this.panels_) {
|
||||
var panel = this.panels_[panelId];
|
||||
// We don't care which targetElement we get, so we just take the last one.
|
||||
targetElement = panel.targetElement;
|
||||
preferBottomPosition = preferBottomPosition && panel.preferBottomPosition;
|
||||
}
|
||||
var status = goog.positioning.OverflowStatus.FAILED;
|
||||
|
||||
// Fix for bug when bubbleContainer and targetElement have
|
||||
// opposite directionality, the bubble should anchor to the END of
|
||||
// the targetElement instead of START.
|
||||
var reverseLayout = (goog.style.isRightToLeft(this.bubbleContainer_) !=
|
||||
goog.style.isRightToLeft(targetElement));
|
||||
|
||||
// Try to put the bubble at the bottom of the target unless the plugin has
|
||||
// requested otherwise.
|
||||
if (preferBottomPosition) {
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.BOTTOM_END :
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
|
||||
}
|
||||
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
// Try to put it at the top of the target if there is not enough
|
||||
// space at the bottom.
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.TOP_END : goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
|
||||
}
|
||||
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
// Put it at the bottom again with adjustment if there is no
|
||||
// enough space at the top.
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.BOTTOM_END :
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Overflow.ADJUST_X |
|
||||
goog.positioning.Overflow.ADJUST_Y);
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
goog.log.warning(this.logger,
|
||||
'reposition(): positionAtAnchor() failed with ' + status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A helper for reposition() - positions the bubble in regards to the position
|
||||
* of the elements the bubble is attached to.
|
||||
* @param {goog.positioning.Corner} targetCorner The corner of
|
||||
* the target element.
|
||||
* @param {goog.positioning.Corner} bubbleCorner The corner of the bubble.
|
||||
* @param {number} overflow Overflow handling mode bitmap,
|
||||
* {@see goog.positioning.Overflow}.
|
||||
* @return {number} Status bitmap, {@see goog.positioning.OverflowStatus}.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.positionAtAnchor_ = function(
|
||||
targetCorner, bubbleCorner, overflow) {
|
||||
var targetElement = null;
|
||||
for (var panelId in this.panels_) {
|
||||
// For now, we use the outermost element. This assumes the multiple
|
||||
// elements this panel is showing for contain each other - in the event
|
||||
// that is not generally the case this may need to be updated to pick
|
||||
// the lowest or highest element depending on targetCorner.
|
||||
var candidate = this.panels_[panelId].targetElement;
|
||||
if (!targetElement || goog.dom.contains(candidate, targetElement)) {
|
||||
targetElement = this.panels_[panelId].targetElement;
|
||||
}
|
||||
}
|
||||
return goog.positioning.positionAtAnchor(
|
||||
targetElement, targetCorner, this.bubbleContainer_,
|
||||
bubbleCorner, null, goog.ui.editor.Bubble.MARGIN_BOX_, overflow);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Private class used to describe a bubble panel.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper used to create the panel.
|
||||
* @param {string} id ID of the panel.
|
||||
* @param {string} type Type of the panel.
|
||||
* @param {string} title Title of the panel.
|
||||
* @param {Element} targetElement Element the panel is showing for.
|
||||
* @param {boolean} preferBottomPosition Whether this panel prefers to show
|
||||
* below the target element.
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.Panel_ = function(dom, id, type, title, targetElement,
|
||||
preferBottomPosition) {
|
||||
/**
|
||||
* The type of bubble panel.
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = type;
|
||||
|
||||
/**
|
||||
* The target element of this bubble panel.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.targetElement = targetElement;
|
||||
|
||||
/**
|
||||
* Whether the panel prefers to be placed below the target element.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.preferBottomPosition = preferBottomPosition;
|
||||
|
||||
/**
|
||||
* The element containing this panel.
|
||||
*/
|
||||
this.element = dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel'), id: id},
|
||||
dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel_title')},
|
||||
title + ':'), // TODO(robbyw): Does this work properly in bidi?
|
||||
dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel_content')}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element in the panel where content should go.
|
||||
*/
|
||||
goog.ui.editor.Bubble.Panel_.prototype.getContentElement = function() {
|
||||
return /** @type {Element} */ (this.element.lastChild);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.editor.EquationEditorDialog');
|
||||
|
||||
goog.require('goog.editor.Command');
|
||||
goog.require('goog.ui.Dialog');
|
||||
goog.require('goog.ui.editor.AbstractDialog');
|
||||
goog.require('goog.ui.editor.EquationEditorOkEvent');
|
||||
goog.require('goog.ui.equation.TexEditor');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Equation editor dialog (based on goog.ui.editor.AbstractDialog).
|
||||
* @param {Object} context The context that this dialog runs in.
|
||||
* @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
|
||||
* dialog's dom structure.
|
||||
* @param {string} equation Initial equation.
|
||||
* @param {string} helpUrl URL pointing to help documentation.
|
||||
* @constructor
|
||||
* @extends {goog.ui.editor.AbstractDialog}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog = function(context, domHelper,
|
||||
equation, helpUrl) {
|
||||
goog.ui.editor.AbstractDialog.call(this, domHelper);
|
||||
this.equationEditor_ =
|
||||
new goog.ui.equation.TexEditor(context, helpUrl, domHelper);
|
||||
this.equationEditor_.render();
|
||||
this.equationEditor_.setEquation(equation);
|
||||
this.equationEditor_.addEventListener(goog.editor.Command.EQUATION,
|
||||
this.onChange_, false, this);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.EquationEditorDialog,
|
||||
goog.ui.editor.AbstractDialog);
|
||||
|
||||
|
||||
/**
|
||||
* The equation editor actual UI.
|
||||
* @type {goog.ui.equation.TexEditor}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.equationEditor_;
|
||||
|
||||
|
||||
/**
|
||||
* The dialog's OK button element.
|
||||
* @type {Element?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.okButton_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.EquationEditorDialog.prototype.createDialogControl =
|
||||
function() {
|
||||
var builder = new goog.ui.editor.AbstractDialog.Builder(this);
|
||||
|
||||
/**
|
||||
* @desc The title of the equation editor dialog.
|
||||
*/
|
||||
var MSG_EE_DIALOG_TITLE = goog.getMsg('Equation Editor');
|
||||
|
||||
/**
|
||||
* @desc Button label for the equation editor dialog for adding
|
||||
* a new equation.
|
||||
*/
|
||||
var MSG_EE_BUTTON_SAVE_NEW = goog.getMsg('Insert equation');
|
||||
|
||||
/**
|
||||
* @desc Button label for the equation editor dialog for saving
|
||||
* a modified equation.
|
||||
*/
|
||||
var MSG_EE_BUTTON_SAVE_MODIFY = goog.getMsg('Save changes');
|
||||
|
||||
var okButtonText = this.equationEditor_.getEquation() ?
|
||||
MSG_EE_BUTTON_SAVE_MODIFY : MSG_EE_BUTTON_SAVE_NEW;
|
||||
|
||||
builder.setTitle(MSG_EE_DIALOG_TITLE)
|
||||
.setContent(this.equationEditor_.getElement())
|
||||
.addOkButton(okButtonText)
|
||||
.addCancelButton();
|
||||
|
||||
return builder.build();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.createOkEvent = function(e) {
|
||||
if (this.equationEditor_.isValid()) {
|
||||
// Equation is not valid, don't close the dialog.
|
||||
return null;
|
||||
}
|
||||
var equationHtml = this.equationEditor_.getHtml();
|
||||
return new goog.ui.editor.EquationEditorOkEvent(equationHtml);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles CHANGE event fired when user changes equation.
|
||||
* @param {goog.ui.equation.ChangeEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.onChange_ = function(e) {
|
||||
if (!this.okButton_) {
|
||||
this.okButton_ = this.getButtonElement(
|
||||
goog.ui.Dialog.DefaultButtonKeys.OK);
|
||||
}
|
||||
this.okButton_.disabled = !e.isValid;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.ui.editor.EquationEditorOkEvent');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.ui.editor.AbstractDialog');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* OK event object for the equation editor dialog.
|
||||
* @param {string} equationHtml html containing the equation to put in the
|
||||
* editable field.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent = function(equationHtml) {
|
||||
this.equationHtml = equationHtml;
|
||||
};
|
||||
goog.inherits(goog.ui.editor.EquationEditorOkEvent,
|
||||
goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Event type.
|
||||
* @type {goog.ui.editor.AbstractDialog.EventType}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent.prototype.type =
|
||||
goog.ui.editor.AbstractDialog.EventType.OK;
|
||||
|
||||
|
||||
/**
|
||||
* HTML containing the equation to put in the editable field.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent.prototype.equationHtml;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Messages common to Editor UI components.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.messages');
|
||||
|
||||
|
||||
/** @desc Link button / bubble caption. */
|
||||
goog.ui.editor.messages.MSG_LINK_CAPTION = goog.getMsg('Link');
|
||||
|
||||
|
||||
/** @desc Title for the dialog that edits a link. */
|
||||
goog.ui.editor.messages.MSG_EDIT_LINK = goog.getMsg('Edit Link');
|
||||
|
||||
|
||||
/** @desc Prompt the user for the text of the link they've written. */
|
||||
goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY = goog.getMsg('Text to display:');
|
||||
|
||||
|
||||
/** @desc Prompt the user for the URL of the link they've created. */
|
||||
goog.ui.editor.messages.MSG_LINK_TO = goog.getMsg('Link to:');
|
||||
|
||||
|
||||
/** @desc Prompt the user to type a web address for their link. */
|
||||
goog.ui.editor.messages.MSG_ON_THE_WEB = goog.getMsg('Web address');
|
||||
|
||||
|
||||
/** @desc More details on what linking to a web address involves.. */
|
||||
goog.ui.editor.messages.MSG_ON_THE_WEB_TIP = goog.getMsg(
|
||||
'Link to a page or file somewhere else on the web');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Text for a button that allows the user to test the link that
|
||||
* they created.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_TEST_THIS_LINK = goog.getMsg('Test this link');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Explanation for how to create a link with the link-editing dialog.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_TR_LINK_EXPLANATION = goog.getMsg(
|
||||
'{$startBold}Not sure what to put in the box?{$endBold} ' +
|
||||
'First, find the page on the web that you want to ' +
|
||||
'link to. (A {$searchEngineLink}search engine{$endLink} ' +
|
||||
'might be useful.) Then, copy the web address from ' +
|
||||
"the box in your browser's address bar, and paste it into " +
|
||||
'the box above.',
|
||||
{'startBold': '<b>',
|
||||
'endBold': '</b>',
|
||||
'searchEngineLink': "<a href='http://www.google.com/' target='_new'>",
|
||||
'endLink': '</a>'});
|
||||
|
||||
|
||||
/** @desc Prompt for the URL of a link that the user is creating. */
|
||||
goog.ui.editor.messages.MSG_WHAT_URL = goog.getMsg(
|
||||
'To what URL should this link go?');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Prompt for an email address, so that the user can create a link
|
||||
* that sends an email.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_ADDRESS = goog.getMsg('Email address');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Explanation of the prompt for an email address in a link.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP = goog.getMsg(
|
||||
'Link to an email address');
|
||||
|
||||
|
||||
/** @desc Error message when the user enters an invalid email address. */
|
||||
goog.ui.editor.messages.MSG_INVALID_EMAIL = goog.getMsg(
|
||||
'Invalid email address');
|
||||
|
||||
|
||||
/**
|
||||
* @desc When the user creates a mailto link, asks them what email
|
||||
* address clicking on this link will send mail to.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_WHAT_EMAIL = goog.getMsg(
|
||||
'To what email address should this link?');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Warning about the dangers of creating links with email
|
||||
* addresses in them.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_EXPLANATION = goog.getMsg(
|
||||
'{$preb}Be careful.{$postb} ' +
|
||||
'Remember that any time you include an email address on a web page, ' +
|
||||
'nasty spammers can find it too.', {'preb': '<b>', 'postb': '</b>'});
|
||||
|
||||
|
||||
/**
|
||||
* @desc Label for the checkbox that allows the user to specify what when this
|
||||
* link is clicked, it should be opened in a new window.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW = goog.getMsg(
|
||||
'Open this link in a new window');
|
||||
|
||||
|
||||
/** @desc Image bubble caption. */
|
||||
goog.ui.editor.messages.MSG_IMAGE_CAPTION = goog.getMsg('Image');
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Tabbed pane with style and functionality specific to
|
||||
* Editor dialogs.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.TabPane');
|
||||
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.Tab');
|
||||
goog.require('goog.ui.TabBar');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new Editor-style tab pane.
|
||||
* @param {goog.dom.DomHelper} dom The dom helper for the window to create this
|
||||
* tab pane in.
|
||||
* @param {string=} opt_caption Optional caption of the tab pane.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.editor.TabPane = function(dom, opt_caption) {
|
||||
goog.base(this, dom);
|
||||
|
||||
/**
|
||||
* The event handler used to register events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
this.registerDisposable(this.eventHandler_);
|
||||
|
||||
/**
|
||||
* The tab bar used to render the tabs.
|
||||
* @type {goog.ui.TabBar}
|
||||
* @private
|
||||
*/
|
||||
this.tabBar_ = new goog.ui.TabBar(goog.ui.TabBar.Location.START,
|
||||
undefined, this.dom_);
|
||||
this.tabBar_.setFocusable(false);
|
||||
|
||||
/**
|
||||
* The content element.
|
||||
* @private
|
||||
*/
|
||||
this.tabContent_ = this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('goog-tab-content')});
|
||||
|
||||
/**
|
||||
* The currently selected radio button.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.selectedRadio_ = null;
|
||||
|
||||
/**
|
||||
* The currently visible tab content.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.visibleContent_ = null;
|
||||
|
||||
|
||||
// Add the caption as the first element in the tab bar.
|
||||
if (opt_caption) {
|
||||
var captionControl = new goog.ui.Control(opt_caption, undefined,
|
||||
this.dom_);
|
||||
captionControl.addClassName(goog.getCssName('tr-tabpane-caption'));
|
||||
captionControl.setEnabled(false);
|
||||
this.tabBar_.addChild(captionControl, true);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.ui.editor.TabPane, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The ID of the content element for the current tab.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.getCurrentTabId = function() {
|
||||
return this.tabBar_.getSelectedTab().getId();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Selects the tab with the given id.
|
||||
* @param {string} id Id of the tab to select.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.setSelectedTabId = function(id) {
|
||||
this.tabBar_.setSelectedTab(this.tabBar_.getChild(id));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a tab to the tab pane.
|
||||
* @param {string} id The id of the tab to add.
|
||||
* @param {string} caption The caption of the tab.
|
||||
* @param {string} tooltip The tooltip for the tab.
|
||||
* @param {string} groupName for the radio button group.
|
||||
* @param {Element} content The content element to show when this tab is
|
||||
* selected.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.addTab = function(id, caption, tooltip,
|
||||
groupName, content) {
|
||||
var radio = this.dom_.createDom(goog.dom.TagName.INPUT,
|
||||
{
|
||||
name: groupName,
|
||||
type: 'radio'
|
||||
});
|
||||
|
||||
var tab = new goog.ui.Tab([radio, this.dom_.createTextNode(caption)],
|
||||
undefined, this.dom_);
|
||||
tab.setId(id);
|
||||
tab.setTooltip(tooltip);
|
||||
this.tabBar_.addChild(tab, true);
|
||||
|
||||
// When you navigate the radio buttons with TAB and then the Arrow keys on
|
||||
// Chrome and FF, you get a CLICK event on them, and the radio button
|
||||
// is selected. You don't get a SELECT at all. We listen for SELECT
|
||||
// nonetheless because it's possible that some browser will issue only
|
||||
// SELECT.
|
||||
this.eventHandler_.listen(radio,
|
||||
[goog.events.EventType.SELECT, goog.events.EventType.CLICK],
|
||||
goog.bind(this.tabBar_.setSelectedTab, this.tabBar_, tab));
|
||||
|
||||
content.id = id + '-tab';
|
||||
this.tabContent_.appendChild(content);
|
||||
goog.style.setElementShown(content, false);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.TabPane.prototype.enterDocument = function() {
|
||||
goog.base(this, 'enterDocument');
|
||||
|
||||
// Get the root element and add a class name to it.
|
||||
var root = this.getElement();
|
||||
goog.dom.classes.add(root, goog.getCssName('tr-tabpane'));
|
||||
|
||||
// Add the tabs.
|
||||
this.addChild(this.tabBar_, true);
|
||||
this.eventHandler_.listen(this.tabBar_, goog.ui.Component.EventType.SELECT,
|
||||
this.handleTabSelect_);
|
||||
|
||||
// Add the tab content.
|
||||
root.appendChild(this.tabContent_);
|
||||
|
||||
// Add an element to clear the tab float.
|
||||
root.appendChild(
|
||||
this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('goog-tab-bar-clear')}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a tab change.
|
||||
* @param {goog.events.Event} e The browser change event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.handleTabSelect_ = function(e) {
|
||||
var tab = /** @type {goog.ui.Tab} */ (e.target);
|
||||
|
||||
// Show the tab content.
|
||||
if (this.visibleContent_) {
|
||||
goog.style.setElementShown(this.visibleContent_, false);
|
||||
}
|
||||
this.visibleContent_ = this.dom_.getElement(tab.getId() + '-tab');
|
||||
goog.style.setElementShown(this.visibleContent_, true);
|
||||
|
||||
// Select the appropriate radio button (and deselect the current one).
|
||||
if (this.selectedRadio_) {
|
||||
this.selectedRadio_.checked = false;
|
||||
}
|
||||
this.selectedRadio_ = tab.getElement().getElementsByTagName(
|
||||
goog.dom.TagName.INPUT)[0];
|
||||
this.selectedRadio_.checked = true;
|
||||
};
|
||||
@@ -0,0 +1,295 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview A class for managing the editor toolbar.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @author jparent@google.com (Julie Parent)
|
||||
* @see ../../demos/editor/editor.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.ToolbarController');
|
||||
|
||||
goog.require('goog.editor.Field');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A class for managing the editor toolbar. Acts as a bridge between
|
||||
* a {@link goog.editor.Field} and a {@link goog.ui.Toolbar}.
|
||||
*
|
||||
* The {@code toolbar} argument must be an instance of {@link goog.ui.Toolbar}
|
||||
* or a subclass. This class doesn't care how the toolbar was created. As
|
||||
* long as one or more controls hosted in the toolbar have IDs that match
|
||||
* built-in {@link goog.editor.Command}s, they will function as expected. It is
|
||||
* the caller's responsibility to ensure that the toolbar is already rendered
|
||||
* or that it decorates an existing element.
|
||||
*
|
||||
*
|
||||
* @param {!goog.editor.Field} field Editable field to be controlled by the
|
||||
* toolbar.
|
||||
* @param {!goog.ui.Toolbar} toolbar Toolbar to control the editable field.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.ToolbarController = function(field, toolbar) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Event handler to listen for field events and user actions.
|
||||
* @type {!goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.handler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* The field instance controlled by the toolbar.
|
||||
* @type {!goog.editor.Field}
|
||||
* @private
|
||||
*/
|
||||
this.field_ = field;
|
||||
|
||||
/**
|
||||
* The toolbar that controls the field.
|
||||
* @type {!goog.ui.Toolbar}
|
||||
* @private
|
||||
*/
|
||||
this.toolbar_ = toolbar;
|
||||
|
||||
/**
|
||||
* Editing commands whose state is to be queried when updating the toolbar.
|
||||
* @type {!Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
this.queryCommands_ = [];
|
||||
|
||||
// Iterate over all buttons, and find those which correspond to
|
||||
// queryable commands. Add them to the list of commands to query on
|
||||
// each COMMAND_VALUE_CHANGE event.
|
||||
this.toolbar_.forEachChild(function(button) {
|
||||
if (button.queryable) {
|
||||
this.queryCommands_.push(this.getComponentId(button.getId()));
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Make sure the toolbar doesn't steal keyboard focus.
|
||||
this.toolbar_.setFocusable(false);
|
||||
|
||||
// Hook up handlers that update the toolbar in response to field events,
|
||||
// and to execute editor commands in response to toolbar events.
|
||||
this.handler_.
|
||||
listen(this.field_, goog.editor.Field.EventType.COMMAND_VALUE_CHANGE,
|
||||
this.updateToolbar).
|
||||
listen(this.toolbar_, goog.ui.Component.EventType.ACTION,
|
||||
this.handleAction);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.ToolbarController, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Closure component ID of the control that corresponds to the
|
||||
* given {@link goog.editor.Command} constant.
|
||||
* Subclasses may override this method if they want to use a custom mapping
|
||||
* scheme from commands to controls.
|
||||
* @param {string} command Editor command.
|
||||
* @return {string} Closure component ID of the corresponding toolbar
|
||||
* control, if any.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getComponentId = function(command) {
|
||||
// The default implementation assumes that the component ID is the same as
|
||||
// the command constant.
|
||||
return command;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link goog.editor.Command} constant
|
||||
* that corresponds to the given Closure component ID. Subclasses may override
|
||||
* this method if they want to use a custom mapping scheme from controls to
|
||||
* commands.
|
||||
* @param {string} id Closure component ID of a toolbar control.
|
||||
* @return {string} Editor command or dialog constant corresponding to the
|
||||
* toolbar control, if any.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getCommand = function(id) {
|
||||
// The default implementation assumes that the component ID is the same as
|
||||
// the command constant.
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the event handler object for the editor toolbar. Useful for classes
|
||||
* that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.events.EventHandler} The event handler object.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getHandler = function() {
|
||||
return this.handler_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the field instance managed by the toolbar. Useful for
|
||||
* classes that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.editor.Field} The field managed by the toolbar.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getField = function() {
|
||||
return this.field_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the toolbar UI component that manages the editor. Useful for
|
||||
* classes that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.ui.Toolbar} The toolbar UI component.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getToolbar = function() {
|
||||
return this.toolbar_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the toolbar is visible.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.isVisible = function() {
|
||||
return this.toolbar_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows or hides the toolbar.
|
||||
* @param {boolean} visible Whether to show or hide the toolbar.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.setVisible = function(visible) {
|
||||
this.toolbar_.setVisible(visible);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the toolbar is enabled.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.isEnabled = function() {
|
||||
return this.toolbar_.isEnabled();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables the toolbar.
|
||||
* @param {boolean} enabled Whether to enable or disable the toolbar.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.setEnabled = function(enabled) {
|
||||
this.toolbar_.setEnabled(enabled);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Programmatically blurs the editor toolbar, un-highlighting the currently
|
||||
* highlighted item, and closing the currently open menu (if any).
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.blur = function() {
|
||||
// We can't just call this.toolbar_.getElement().blur(), because the toolbar
|
||||
// element itself isn't focusable, so goog.ui.Container#handleBlur isn't
|
||||
// registered to handle blur events.
|
||||
this.toolbar_.handleBlur(null);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.ToolbarController.prototype.disposeInternal = function() {
|
||||
goog.ui.editor.ToolbarController.superClass_.disposeInternal.call(this);
|
||||
if (this.handler_) {
|
||||
this.handler_.dispose();
|
||||
delete this.handler_;
|
||||
}
|
||||
if (this.toolbar_) {
|
||||
this.toolbar_.dispose();
|
||||
delete this.toolbar_;
|
||||
}
|
||||
delete this.field_;
|
||||
delete this.queryCommands_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the toolbar in response to editor events. Specifically, updates
|
||||
* button states based on {@code COMMAND_VALUE_CHANGE} events, reflecting the
|
||||
* effective formatting of the selection.
|
||||
* @param {goog.events.Event} e Editor event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.updateToolbar = function(e) {
|
||||
if (!this.toolbar_.isEnabled() ||
|
||||
!this.dispatchEvent(goog.ui.Component.EventType.CHANGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var state;
|
||||
|
||||
/** @preserveTry */
|
||||
try {
|
||||
/** @type {Array.<string>} */
|
||||
e.commands; // Added by dispatchEvent.
|
||||
|
||||
// If the COMMAND_VALUE_CHANGE event specifies which commands changed
|
||||
// state, then we only need to update those ones, otherwise update all
|
||||
// commands.
|
||||
state = /** @type {Object} */ (
|
||||
this.field_.queryCommandValue(e.commands || this.queryCommands_));
|
||||
} catch (ex) {
|
||||
// TODO(attila): Find out when/why this happens.
|
||||
state = {};
|
||||
}
|
||||
|
||||
this.updateToolbarFromState(state);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the toolbar to reflect a given state.
|
||||
* @param {Object} state Object mapping editor commands to values.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.updateToolbarFromState =
|
||||
function(state) {
|
||||
for (var command in state) {
|
||||
var button = this.toolbar_.getChild(this.getComponentId(command));
|
||||
if (button) {
|
||||
var value = state[command];
|
||||
if (button.updateFromValue) {
|
||||
button.updateFromValue(value);
|
||||
} else {
|
||||
button.setChecked(!!value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles {@code ACTION} events dispatched by toolbar buttons in response to
|
||||
* user actions by executing the corresponding field command.
|
||||
* @param {goog.events.Event} e Action event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.handleAction = function(e) {
|
||||
var command = this.getCommand(e.target.getId());
|
||||
this.field_.execCommand(command, e.target.getValue());
|
||||
};
|
||||
@@ -0,0 +1,440 @@
|
||||
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Generic factory functions for creating the building blocks for
|
||||
* an editor toolbar.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @author jparent@google.com (Julie Parent)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.ToolbarFactory');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.string.Unicode');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Container');
|
||||
goog.require('goog.ui.Option');
|
||||
goog.require('goog.ui.Toolbar');
|
||||
goog.require('goog.ui.ToolbarButton');
|
||||
goog.require('goog.ui.ToolbarColorMenuButton');
|
||||
goog.require('goog.ui.ToolbarMenuButton');
|
||||
goog.require('goog.ui.ToolbarRenderer');
|
||||
goog.require('goog.ui.ToolbarSelect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Takes a font spec (e.g. "Arial, Helvetica, sans-serif") and returns the
|
||||
* primary font name, normalized to lowercase (e.g. "arial").
|
||||
* @param {string} fontSpec Font specification.
|
||||
* @return {string} The primary font name, in lowercase.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getPrimaryFont = function(fontSpec) {
|
||||
var i = fontSpec.indexOf(',');
|
||||
var fontName = (i != -1 ? fontSpec.substring(0, i) : fontSpec).toLowerCase();
|
||||
// Strip leading/trailing quotes from the font name (bug 1050118).
|
||||
return goog.string.stripQuotes(fontName, '"\'');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds fonts to the given font menu button. The argument must be an
|
||||
* array of font descriptor objects, each of which must have the following
|
||||
* attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the font menu (e.g. 'Tahoma')
|
||||
* <li>{@code value} - Value for the corresponding 'font-family' CSS style
|
||||
* (e.g. 'Tahoma, Arial, sans-serif')
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button Font menu button.
|
||||
* @param {!Array.<{caption: string, value: string}>} fonts Array of
|
||||
* font descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFonts = function(button, fonts) {
|
||||
goog.array.forEach(fonts, function(font) {
|
||||
goog.ui.editor.ToolbarFactory.addFont(button, font.caption, font.value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given font menu button. The first font listed in
|
||||
* the {@code value} argument is considered the font ID, so adding two items
|
||||
* whose CSS style starts with the same font may lead to unpredictable results.
|
||||
* @param {!goog.ui.Select} button Font menu button.
|
||||
* @param {string} caption Caption to show for the font menu.
|
||||
* @param {string} value Value for the corresponding 'font-family' CSS style.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFont = function(button, caption, value) {
|
||||
// The font ID is the first font listed in the CSS style, normalized to
|
||||
// lowercase.
|
||||
var id = goog.ui.editor.ToolbarFactory.getPrimaryFont(value);
|
||||
|
||||
// Construct the option, and add it to the button.
|
||||
var option = new goog.ui.Option(caption, value, button.getDomHelper());
|
||||
option.setId(id);
|
||||
button.addItem(option);
|
||||
|
||||
// Captions are shown in their own font.
|
||||
option.getContentElement().style.fontFamily = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds font sizes to the given font size menu button. The argument must
|
||||
* be an array of font size descriptor objects, each of which must have the
|
||||
* following attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the font size menu (e.g. 'Huge')
|
||||
* <li>{@code value} - Value for the corresponding HTML font size (e.g. 6)
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button Font size menu button.
|
||||
* @param {!Array.<{caption: string, value:number}>} sizes Array of font
|
||||
* size descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFontSizes = function(button, sizes) {
|
||||
goog.array.forEach(sizes, function(size) {
|
||||
goog.ui.editor.ToolbarFactory.addFontSize(button, size.caption, size.value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given font size menu button. The {@code value}
|
||||
* argument must be a legacy HTML font size in the 0-7 range.
|
||||
* @param {!goog.ui.Select} button Font size menu button.
|
||||
* @param {string} caption Caption to show in the font size menu.
|
||||
* @param {number} value Value for the corresponding HTML font size.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFontSize = function(button, caption, value) {
|
||||
// Construct the option, and add it to the button.
|
||||
var option = new goog.ui.Option(caption, value, button.getDomHelper());
|
||||
button.addItem(option);
|
||||
|
||||
// Adjust the font size of the menu item and the height of the checkbox
|
||||
// element after they've been rendered by addItem(). Captions are shown in
|
||||
// the corresponding font size, and lining up the checkbox is tricky.
|
||||
var content = option.getContentElement();
|
||||
content.style.fontSize =
|
||||
goog.ui.editor.ToolbarFactory.getPxFromLegacySize(value) + 'px';
|
||||
content.firstChild.style.height = '1.1em';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a legacy font size specification into an equivalent pixel size.
|
||||
* For example, {@code <font size="6">} is {@code font-size: 32px;}, etc.
|
||||
* @param {number} fontSize Legacy font size spec in the 0-7 range.
|
||||
* @return {number} Equivalent pixel size.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getPxFromLegacySize = function(fontSize) {
|
||||
return goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_[fontSize] || 10;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a pixel font size specification into an equivalent legacy size.
|
||||
* For example, {@code font-size: 32px;} is {@code <font size="6">}, etc.
|
||||
* If the given pixel size doesn't exactly match one of the legacy sizes, -1 is
|
||||
* returned.
|
||||
* @param {number} px Pixel font size.
|
||||
* @return {number} Equivalent legacy size spec in the 0-7 range, or -1 if none
|
||||
* exists.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getLegacySizeFromPx = function(px) {
|
||||
// Use lastIndexOf to get the largest legacy size matching the pixel size
|
||||
// (most notably returning 1 instead of 0 for 10px).
|
||||
return goog.array.lastIndexOf(
|
||||
goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_, px);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Map of legacy font sizes (0-7) to equivalent pixel sizes.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ =
|
||||
[10, 10, 13, 16, 18, 24, 32, 48];
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds format options to the given "Format block" menu button. The
|
||||
* argument must be an array of format option descriptor objects, each of
|
||||
* which must have the following attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the menu (e.g. 'Minor heading')
|
||||
* <li>{@code command} - Corresponding {@link goog.dom.TagName} (e.g.
|
||||
* 'H4')
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button "Format block" menu button.
|
||||
* @param {!Array.<{caption: string, command: goog.dom.TagName}>} formats Array
|
||||
* of format option descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) {
|
||||
goog.array.forEach(formats, function(format) {
|
||||
goog.ui.editor.ToolbarFactory.addFormatOption(button, format.caption,
|
||||
format.command);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given "Format block" menu button.
|
||||
* @param {!goog.ui.Select} button "Format block" menu button.
|
||||
* @param {string} caption Caption to show in the menu.
|
||||
* @param {goog.dom.TagName} tag Corresponding block format tag.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) {
|
||||
// Construct the option, and add it to the button.
|
||||
// TODO(attila): Create boring but functional menu item for now...
|
||||
var buttonDom = button.getDomHelper();
|
||||
var option = new goog.ui.Option(buttonDom.createDom(goog.dom.TagName.DIV,
|
||||
null, caption), tag, buttonDom);
|
||||
option.setId(tag);
|
||||
button.addItem(option);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link goog.ui.Toolbar} containing the specified set of
|
||||
* toolbar buttons, and renders it into the given parent element. Each
|
||||
* item in the {@code items} array must a {@link goog.ui.Control}.
|
||||
* @param {!Array.<goog.ui.Control>} items Toolbar items; each must
|
||||
* be a {@link goog.ui.Control}.
|
||||
* @param {!Element} elem Toolbar parent element.
|
||||
* @param {boolean=} opt_isRightToLeft Whether the editor chrome is
|
||||
* right-to-left; defaults to the directionality of the toolbar parent
|
||||
* element.
|
||||
* @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent
|
||||
* element.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeToolbar = function(items, elem,
|
||||
opt_isRightToLeft) {
|
||||
var domHelper = goog.dom.getDomHelper(elem);
|
||||
|
||||
// Create an empty horizontal toolbar using the default renderer.
|
||||
var toolbar = new goog.ui.Toolbar(goog.ui.ToolbarRenderer.getInstance(),
|
||||
goog.ui.Container.Orientation.HORIZONTAL, domHelper);
|
||||
|
||||
// Optimization: Explicitly test for the directionality of the parent
|
||||
// element here, so we can set it for both the toolbar and its children,
|
||||
// saving a lot of expensive calls to goog.style.isRightToLeft() during
|
||||
// rendering.
|
||||
var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem);
|
||||
toolbar.setRightToLeft(isRightToLeft);
|
||||
|
||||
// Optimization: Set the toolbar to non-focusable before it is rendered,
|
||||
// to avoid creating unnecessary keyboard event handler objects.
|
||||
toolbar.setFocusable(false);
|
||||
|
||||
for (var i = 0, button; button = items[i]; i++) {
|
||||
// Optimization: Set the button to non-focusable before it is rendered,
|
||||
// to avoid creating unnecessary keyboard event handler objects. Also set
|
||||
// the directionality of the button explicitly, to avoid expensive calls
|
||||
// to goog.style.isRightToLeft() during rendering.
|
||||
button.setSupportedState(goog.ui.Component.State.FOCUSED, false);
|
||||
button.setRightToLeft(isRightToLeft);
|
||||
toolbar.addChild(button, true);
|
||||
}
|
||||
|
||||
toolbar.render(elem);
|
||||
return toolbar;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a toolbar button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Button} A toolbar button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a toggle button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element. The button
|
||||
* returned has checkbox-like toggle semantics.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Button} A toggle button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeToggleButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = goog.ui.editor.ToolbarFactory.makeButton(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper);
|
||||
button.setSupportedState(goog.ui.Component.State.CHECKED, true);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a menu button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element. The button
|
||||
* returned doesn't have an actual menu attached; use {@link
|
||||
* goog.ui.MenuButton#setMenu} to attach a {@link goog.ui.Menu} to the
|
||||
* button.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.MenuButton} A menu button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeMenuButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarMenuButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a select button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's root element. The button
|
||||
* returned doesn't have an actual menu attached; use {@link
|
||||
* goog.ui.Select#setMenu} to attach a {@link goog.ui.Menu} containing
|
||||
* {@link goog.ui.Option}s to the select button.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption; used as the
|
||||
* default caption when nothing is selected.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the button's
|
||||
* root element.
|
||||
* @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
|
||||
* defaults to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Select} A select button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeSelectButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarSelect(null, null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
if (opt_classNames) {
|
||||
// Unlike the other button types, for goog.ui.Select buttons we apply the
|
||||
// extra class names to the root element, because for select buttons the
|
||||
// caption isn't stable (as it changes each time the selection changes).
|
||||
goog.array.forEach(opt_classNames.split(/\s+/), button.addClassName,
|
||||
button);
|
||||
}
|
||||
button.addClassName(goog.getCssName('goog-toolbar-select'));
|
||||
button.setDefaultCaption(caption);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a color menu button with the given ID, tooltip, and caption.
|
||||
* Applies any custom CSS class names to the button's caption element. The
|
||||
* button is created with a default color menu containing standard color
|
||||
* palettes.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in toolbar buttons, but can be anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
|
||||
* defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer}
|
||||
* if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.ColorMenuButton} A color menu button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeColorMenuButton = function(id, tooltip,
|
||||
caption, opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarColorMenuButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new DIV that wraps a button caption, optionally applying CSS
|
||||
* class names to it. Used as a helper function in button factory methods.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the DIV that
|
||||
* wraps the caption (if any).
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!Element} DIV that wraps the caption.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.createContent_ = function(caption, opt_classNames,
|
||||
opt_domHelper) {
|
||||
// FF2 doesn't like empty DIVs, especially when rendered right-to-left.
|
||||
if ((!caption || caption == '') && goog.userAgent.GECKO &&
|
||||
!goog.userAgent.isVersionOrHigher('1.9a')) {
|
||||
caption = goog.string.Unicode.NBSP;
|
||||
}
|
||||
return (opt_domHelper || goog.dom.getDomHelper()).createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
opt_classNames ? {'class' : opt_classNames} : null, caption);
|
||||
};
|
||||
Reference in New Issue
Block a user