aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/closure-library/closure/goog/editor/icontent.js
blob: 4316bd5e59758f1c6796e6878fe38419c46fae06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// Copyright 2007 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.
// All Rights Reserved.

/**
 * @fileoverview Static functions for writing the contents of an iframe-based
 * editable field. These vary significantly from browser to browser. Uses
 * strings and document.write instead of DOM manipulation, because
 * iframe-loading is a performance bottleneck.
 *
 */

goog.provide('goog.editor.icontent');
goog.provide('goog.editor.icontent.FieldFormatInfo');
goog.provide('goog.editor.icontent.FieldStyleInfo');

goog.require('goog.editor.BrowserFeature');
goog.require('goog.style');
goog.require('goog.userAgent');



/**
 * A data structure for storing simple rendering info about a field.
 *
 * @param {string} fieldId The id of the field.
 * @param {boolean} standards Whether the field should be rendered in
 *     standards mode.
 * @param {boolean} blended Whether the field is in blended mode.
 * @param {boolean} fixedHeight Whether the field is in fixedHeight mode.
 * @param {Object=} opt_extraStyles Other style attributes for the field,
 *     represented as a map of strings.
 * @constructor
 */
goog.editor.icontent.FieldFormatInfo = function(fieldId, standards, blended,
    fixedHeight, opt_extraStyles) {
  this.fieldId_ = fieldId;
  this.standards_ = standards;
  this.blended_ = blended;
  this.fixedHeight_ = fixedHeight;
  this.extraStyles_ = opt_extraStyles || {};
};



/**
 * A data structure for storing simple info about the styles of a field.
 * Only needed in Firefox/Blended mode.
 * @param {Element} wrapper The wrapper div around a field.
 * @param {string} css The css for a field.
 * @constructor
 */
goog.editor.icontent.FieldStyleInfo = function(wrapper, css) {
  this.wrapper_ = wrapper;
  this.css_ = css;
};


/**
 * Whether to always use standards-mode iframes.
 * @type {boolean}
 * @private
 */
goog.editor.icontent.useStandardsModeIframes_ = false;


/**
 * Sets up goog.editor.icontent to always use standards-mode iframes.
 */
goog.editor.icontent.forceStandardsModeIframes = function() {
  goog.editor.icontent.useStandardsModeIframes_ = true;
};


/**
 * Generate the initial iframe content.
 * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about
 *     the field.
 * @param {string} bodyHtml The HTML to insert as the iframe body.
 * @param {goog.editor.icontent.FieldStyleInfo?} style Style info about
 *     the field, if needed.
 * @return {string} The initial IFRAME content HTML.
 * @private
 */
goog.editor.icontent.getInitialIframeContent_ =
    function(info, bodyHtml, style) {
  var html = [];

  if (info.blended_ && info.standards_ ||
      goog.editor.icontent.useStandardsModeIframes_) {
    html.push('<!DOCTYPE HTML>');
  }

  // <HTML>
  // NOTE(user): Override min-widths that may be set for all
  // HTML/BODY nodes. A similar workaround is below for the <body> tag. This
  // can happen if the host page includes a rule like this in its CSS:
  //
  // html, body {min-width: 500px}
  //
  // In this case, the iframe's <html> and/or <body> may be affected. This was
  // part of the problem observed in http://b/5674613. (The other part of that
  // problem had to do with the presence of a spurious horizontal scrollbar,
  // which caused the editor height to be computed incorrectly.)
  html.push('<html style="background:none transparent;min-width:0;');

  // Make sure that the HTML element's height has the
  // correct value as the body element's percentage height is made relative
  // to the HTML element's height.
  // For fixed-height it should be 100% since we want the body to fill the
  // whole height. For growing fields it should be auto since we want the
  // body to size to its content.
  if (info.blended_) {
    html.push('height:', info.fixedHeight_ ? '100%' : 'auto');
  }
  html.push('">');

  // <HEAD><STYLE>

  // IE/Safari whitebox need styles set only iff the client specifically
  // requested them.
  html.push('<head><style>');
  if (style && style.css_) {
    html.push(style.css_);
  }

  // Firefox blended needs to inherit all the css from the original page.
  // Firefox standards mode needs to set extra style for images.
  if (goog.userAgent.GECKO && info.standards_) {
    // Standards mode will collapse broken images.  This means that they
    // can never be removed from the field.  This style forces the images
    // to render as a broken image icon, sized based on the width and height
    // of the image.
    // TODO(user): Make sure we move this into a contentEditable code
    // path if there ever is one for FF.
    html.push(' img {-moz-force-broken-image-icon: 1;}');
  }

  html.push('</style></head>');

  // <BODY>
  // Hidefocus is needed to ensure that IE7 doesn't show the dotted, focus
  // border when you tab into the field.
  html.push('<body g_editable="true" hidefocus="true" ');
  if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    html.push('contentEditable ');
  }

  html.push('class="editable ');

  // TODO: put the field's original ID on the body and stop using ID as a
  // way of getting the pointer to the field in the iframe now that it's
  // always the body.
  html.push('" id="', info.fieldId_, '" style="min-width:0;');

  if (goog.userAgent.GECKO && info.blended_) {
    // IMPORTANT: Apply the css from the body then all of the clearing
    // CSS to make sure the clearing CSS overrides (e.g. if the body
    // has a 3px margin, we want to make sure to override it with 0px.
    html.push(

        // margin should not be applied to blended mode because the margin is
        // outside the iframe
        // In whitebox mode, we want to leave the margin to the default so
        // there is a nice margin around the text.
        ';width:100%;border:0;margin:0;background:none transparent;',

        // In standards-mode, height 100% makes the body size to its
        // parent html element, but in quirks mode, we want auto because
        // 100% makes it size to the containing window even if the html
        // element is smaller.
        // TODO: Fixed height, standards mode, CSS_WRITING, with margins on the
        // paragraphs has a scrollbar when it doesn't need it.  Putting the
        // height to auto seems to fix it.  Figure out if we should always
        // just use auto?
        ';height:', info.standards_ ? '100%' : 'auto');

    // Only do this for mozilla. IE6 standards mode has a rendering bug when
    // there are scrollbars and the body's overflow property is auto
    if (info.fixedHeight_) {
      html.push(';overflow:auto');
    } else {
      html.push(';overflow-y:hidden;overflow-x:auto');
    }
  }

  // Hide the native focus rect in Opera.
  if (goog.userAgent.OPERA) {
    html.push(';outline:hidden');
  }

  for (var key in info.extraStyles_) {
    html.push(';' + key + ':' + info.extraStyles_[key]);
  }

  html.push('">', bodyHtml, '</body></html>');

  return html.join('');
};


/**
 * Write the initial iframe content in normal mode.
 * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about
 *     the field.
 * @param {string} bodyHtml The HTML to insert as the iframe body.
 * @param {goog.editor.icontent.FieldStyleInfo?} style Style info about
 *     the field, if needed.
 * @param {HTMLIFrameElement} iframe The iframe.
 */
goog.editor.icontent.writeNormalInitialBlendedIframe =
    function(info, bodyHtml, style, iframe) {
  // Firefox blended needs to inherit all the css from the original page.
  // Firefox standards mode needs to set extra style for images.
  if (info.blended_) {
    var field = style.wrapper_;
    // If there is padding on the original field, then the iFrame will be
    // positioned inside the padding by default.  We don't want this, as it
    // causes the contents to appear to shift, and also causes the
    // scrollbars to appear inside the padding.
    //
    // To compensate, we set the iframe margins to offset the padding.
    var paddingBox = goog.style.getPaddingBox(field);
    if (paddingBox.top || paddingBox.left ||
        paddingBox.right || paddingBox.bottom) {
      goog.style.setStyle(iframe, 'margin',
          (-paddingBox.top) + 'px ' +
          (-paddingBox.right) + 'px ' +
          (-paddingBox.bottom) + 'px ' +
          (-paddingBox.left) + 'px');
    }
  }

  goog.editor.icontent.writeNormalInitialIframe(
      info, bodyHtml, style, iframe);
};


/**
 * Write the initial iframe content in normal mode.
 * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about
 *     the field.
 * @param {string} bodyHtml The HTML to insert as the iframe body.
 * @param {goog.editor.icontent.FieldStyleInfo?} style Style info about
 *     the field, if needed.
 * @param {HTMLIFrameElement} iframe The iframe.
 */
goog.editor.icontent.writeNormalInitialIframe =
    function(info, bodyHtml, style, iframe) {

  var html = goog.editor.icontent.getInitialIframeContent_(
      info, bodyHtml, style);

  var doc = goog.dom.getFrameContentDocument(iframe);
  doc.open();
  doc.write(html);
  doc.close();
};


/**
 * Write the initial iframe content in IE/HTTPS mode.
 * @param {goog.editor.icontent.FieldFormatInfo} info Formatting info about
 *     the field.
 * @param {Document} doc The iframe document.
 * @param {string} bodyHtml The HTML to insert as the iframe body.
 */
goog.editor.icontent.writeHttpsInitialIframe = function(info, doc, bodyHtml) {
  var body = doc.body;

  // For HTTPS we already have a document with a doc type and a body element
  // and don't want to create a new history entry which can cause data loss if
  // the user clicks the back button.
  if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
    body.contentEditable = true;
  }
  body.className = 'editable';
  body.setAttribute('g_editable', true);
  body.hideFocus = true;
  body.id = info.fieldId_;

  goog.style.setStyle(body, info.extraStyles_);
  body.innerHTML = bodyHtml;
};