Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// Copyright 2006 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 Class to encapsulate an editable field that blends in with
* the style of the page. The field can be fixed height, grow with its
* contents, or have a min height after which it grows to its contents.
* This is a goog.editor.Field, but with blending and sizing capabilities,
* and avoids using an iframe whenever possible.
*
* @author [email protected] (Nick Santos)
* @see ../demos/editor/seamlessfield.html
*/
goog.provide('goog.editor.SeamlessField');
goog.require('goog.cssom.iframe.style');
goog.require('goog.dom');
goog.require('goog.dom.Range');
goog.require('goog.dom.TagName');
goog.require('goog.dom.safe');
goog.require('goog.editor.BrowserFeature');
goog.require('goog.editor.Field');
goog.require('goog.editor.icontent');
goog.require('goog.editor.icontent.FieldFormatInfo');
goog.require('goog.editor.icontent.FieldStyleInfo');
goog.require('goog.editor.node');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.html.uncheckedconversions');
goog.require('goog.log');
goog.require('goog.string.Const');
goog.require('goog.style');
/**
* This class encapsulates an editable field that blends in with the
* surrounding page.
* To see events fired by this object, please see the base class.
*
* @param {string} id An identifer for the field. This is used to find the
* field and the element associated with this field.
* @param {Document=} opt_doc The document that the element with the given
* id can be found it.
* @constructor
* @extends {goog.editor.Field}
*/
goog.editor.SeamlessField = function(id, opt_doc) {
goog.editor.Field.call(this, id, opt_doc);
};
goog.inherits(goog.editor.SeamlessField, goog.editor.Field);
/**
* @override
*/
goog.editor.SeamlessField.prototype.logger =
goog.log.getLogger('goog.editor.SeamlessField');
// Functions dealing with field sizing.
/**
* The key used for listening for the "dragover" event.
* @type {goog.events.Key}
* @private
*/
goog.editor.SeamlessField.prototype.listenForDragOverEventKey_;
/**
* The key used for listening for the iframe "load" event.
* @type {goog.events.Key}
* @private
*/
goog.editor.SeamlessField.prototype.listenForIframeLoadEventKey_;
/**
* Sets the min height of this editable field's iframe. Only used in growing
* mode when an iframe is used. This will cause an immediate field sizing to
* update the field if necessary based on the new min height.
* @param {number} height The min height specified as a number of pixels,
* e.g., 75.
*/
goog.editor.SeamlessField.prototype.setMinHeight = function(height) {
if (height == this.minHeight_) {
// Do nothing if the min height isn't changing.
return;
}
this.minHeight_ = height;
if (this.usesIframe()) {
this.doFieldSizingGecko();
}
};
/**
* Whether the field should be rendered with a fixed height, or should expand
* to fit its contents.
* @type {boolean}
* @private
*/
goog.editor.SeamlessField.prototype.isFixedHeight_ = false;
/**
* Whether the fixed-height handling has been overridden manually.
* @type {boolean}
* @private
*/
goog.editor.SeamlessField.prototype.isFixedHeightOverridden_ = false;
/**
* @return {boolean} Whether the field should be rendered with a fixed
* height, or should expand to fit its contents.
* @override
*/
goog.editor.SeamlessField.prototype.isFixedHeight = function() {
return this.isFixedHeight_;
};
/**
* @param {boolean} newVal Explicitly set whether the field should be
* of a fixed-height. This overrides auto-detection.
*/
goog.editor.SeamlessField.prototype.overrideFixedHeight = function(newVal) {
this.isFixedHeight_ = newVal;
this.isFixedHeightOverridden_ = true;
};
/**
* Auto-detect whether the current field should have a fixed height.
* @private
*/
goog.editor.SeamlessField.prototype.autoDetectFixedHeight_ = function() {
if (!this.isFixedHeightOverridden_) {
var originalElement = this.getOriginalElement();
if (originalElement) {
this.isFixedHeight_ =
goog.style.getComputedOverflowY(originalElement) == 'auto';
}
}
};
/**
* Resize the iframe in response to the wrapper div changing size.
* @private
*/
goog.editor.SeamlessField.prototype.handleOuterDocChange_ = function() {
if (this.isEventStopped(goog.editor.Field.EventType.CHANGE)) {
return;
}
this.sizeIframeToWrapperGecko_();
};
/**
* Sizes the iframe to its body's height.
* @private
*/
goog.editor.SeamlessField.prototype.sizeIframeToBodyHeightGecko_ = function() {
if (this.acquireSizeIframeLockGecko_()) {
var resized = false;
var ifr = this.getEditableIframe();
if (ifr) {
var fieldHeight = this.getIframeBodyHeightGecko_();
if (this.minHeight_) {
fieldHeight = Math.max(fieldHeight, this.minHeight_);
}
if (parseInt(goog.style.getStyle(ifr, 'height'), 10) != fieldHeight) {
ifr.style.height = fieldHeight + 'px';
resized = true;
}
}
this.releaseSizeIframeLockGecko_();
if (resized) {
this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
}
}
};
/**
* @return {number} The height of the editable iframe's body.
* @private
*/
goog.editor.SeamlessField.prototype.getIframeBodyHeightGecko_ = function() {
var ifr = this.getEditableIframe();
var body = ifr.contentDocument.body;
var htmlElement = /** @type {!HTMLElement} */ (body.parentNode);
// If the iframe's height is 0, then the offsetHeight/scrollHeight of the
// HTML element in the iframe can be totally wack (i.e. too large
// by 50-500px). Also, in standard's mode the clientHeight is 0.
if (parseInt(goog.style.getStyle(ifr, 'height'), 10) === 0) {
goog.style.setStyle(ifr, 'height', 1 + 'px');
}
var fieldHeight;
if (goog.editor.node.isStandardsMode(body)) {
// If in standards-mode,
// grab the HTML element as it will contain all the field's
// contents. The body's height, for example, will not include that of
// floated images at the bottom in standards mode.
// Note that this value include all scrollbars *except* for scrollbars
// on the HTML element itself.
fieldHeight = htmlElement.offsetHeight;
} else {
// In quirks-mode, the body-element always seems
// to size to the containing window. The html-element however,
// sizes to the content, and can thus end up with a value smaller
// than its child body-element if the content is shrinking.
// We want to make the iframe shrink too when the content shrinks,
// so rather than size the iframe to the body-element, size it to
// the html-element.
fieldHeight = htmlElement.scrollHeight;
// If there is a horizontal scroll, add in the thickness of the
// scrollbar.
if (htmlElement.clientHeight != htmlElement.offsetHeight) {
fieldHeight += goog.editor.SeamlessField.getScrollbarWidth_();
}
}
return fieldHeight;
};
/**
* Grabs the width of a scrollbar from the browser and caches the result.
* @return {number} The scrollbar width in pixels.
* @private
*/
goog.editor.SeamlessField.getScrollbarWidth_ = function() {
return goog.editor.SeamlessField.scrollbarWidth_ ||
(goog.editor.SeamlessField.scrollbarWidth_ =
goog.style.getScrollbarWidth());
};
/**
* Sizes the iframe to its container div's width. The width of the div
* is controlled by its containing context, not by its contents.
* if it extends outside of it's contents, then it gets a horizontal scroll.
* @private
*/
goog.editor.SeamlessField.prototype.sizeIframeToWrapperGecko_ = function() {
if (this.acquireSizeIframeLockGecko_()) {
var ifr = this.getEditableIframe();
var field = this.getElement();
var resized = false;
if (ifr && field) {
var fieldPaddingBox;
var widthDiv = /** @type {!HTMLElement} */ (ifr.parentNode);
var width = widthDiv.offsetWidth;
if (parseInt(goog.style.getStyle(ifr, 'width'), 10) != width) {
fieldPaddingBox = goog.style.getPaddingBox(field);
ifr.style.width = width + 'px';
field.style.width =
width - fieldPaddingBox.left - fieldPaddingBox.right + 'px';
resized = true;
}
var height = widthDiv.offsetHeight;
if (this.isFixedHeight() &&
parseInt(goog.style.getStyle(ifr, 'height'), 10) != height) {
if (!fieldPaddingBox) {
fieldPaddingBox = goog.style.getPaddingBox(field);
}
ifr.style.height = height + 'px';
field.style.height =
height - fieldPaddingBox.top - fieldPaddingBox.bottom + 'px';
resized = true;
}
}
this.releaseSizeIframeLockGecko_();
if (resized) {
this.dispatchEvent(goog.editor.Field.EventType.IFRAME_RESIZED);
}
}
};
/**
* Perform all the sizing immediately.
*/
goog.editor.SeamlessField.prototype.doFieldSizingGecko = function() {
// Because doFieldSizingGecko can be called after a setTimeout
// it is possible that the field has been destroyed before this call
// to do the sizing is executed. Check for field existence and do nothing
// if it has already been destroyed.
if (this.getElement()) {
// The order of operations is important here. Sizing the iframe to the
// wrapper could cause the width to change, which could change the line
// wrapping, which could change the body height. So we need to do that
// first, then size the iframe to fit the body height.
this.sizeIframeToWrapperGecko_();
if (!this.isFixedHeight()) {
this.sizeIframeToBodyHeightGecko_();
}
}
};
/**
* Acquires a lock on resizing the field iframe. This is used to ensure that
* modifications we make while in a mutation event handler don't cause
* infinite loops.
* @return {boolean} False if the lock is already acquired.
* @private
*/
goog.editor.SeamlessField.prototype.acquireSizeIframeLockGecko_ = function() {
if (this.sizeIframeLock_) {
return false;
}
return this.sizeIframeLock_ = true;
};
/**
* Releases a lock on resizing the field iframe. This is used to ensure that
* modifications we make while in a mutation event handler don't cause
* infinite loops.
* @private
*/
goog.editor.SeamlessField.prototype.releaseSizeIframeLockGecko_ = function() {
this.sizeIframeLock_ = false;
};
// Functions dealing with blending in with the surrounding page.
/**
* String containing the css rules that, if applied to a document's body,
* would style that body as if it were the original element we made editable.
* See goog.cssom.iframe.style.getElementContext for more details.
* @type {string}
* @private
*/
goog.editor.SeamlessField.prototype.iframeableCss_ = '';
/**
* Gets the css rules that should be used to style an iframe's body as if it
* were the original element that we made editable.
* @param {boolean=} opt_forceRegeneration Set to true to not read the cached
* copy and instead completely regenerate the css rules.
* @return {string} The string containing the css rules to use.
*/
goog.editor.SeamlessField.prototype.getIframeableCss = function(
opt_forceRegeneration) {
if (!this.iframeableCss_ || opt_forceRegeneration) {
var originalElement = this.getOriginalElement();
if (originalElement) {
this.iframeableCss_ = goog.cssom.iframe.style.getElementContext(
originalElement, opt_forceRegeneration);
}
}
return this.iframeableCss_;
};
/**
* Sets the css rules that should be used inside the editable iframe.
* Note: to clear the css cache between makeNotEditable/makeEditable,
* call this with "" as iframeableCss.
* TODO(user): Unify all these css setting methods + Nick's open
* CL. This is getting ridiculous.
* @param {string} iframeableCss String containing the css rules to use.
*/
goog.editor.SeamlessField.prototype.setIframeableCss = function(iframeableCss) {
this.iframeableCss_ = iframeableCss;
};
/**
* Used to ensure that CSS stylings are only installed once for none
* iframe seamless mode.
* TODO(user): Make it a formal part of the API that you can only
* set one set of styles globally.
* In seamless, non-iframe mode, all the stylings would go in the
* same document and conflict.
* @type {boolean}
* @private
*/
goog.editor.SeamlessField.haveInstalledCss_ = false;
/**
* Applies CSS from the wrapper-div to the field iframe.
*/
goog.editor.SeamlessField.prototype.inheritBlendedCSS = function() {
// No-op if the field isn't using an iframe.
if (!this.usesIframe()) {
return;
}
var field = this.getElement();
var head = goog.dom.getDomHelper(field).getElementsByTagNameAndClass(
goog.dom.TagName.HEAD)[0];
if (head) {
// We created this , and we know the only thing we put in there
// is a