aboutsummaryrefslogtreecommitdiff
path: root/sources/core/dom/document.js
blob: f287245e64d820d3281d57d1641f8d16ebfdde6f (plain) (blame)
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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/**
 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

/**
 * @fileOverview Defines the {@link CKEDITOR.dom.document} class which
 *		represents a DOM document.
 */

/**
 * Represents a DOM document.
 *
 *		var document = new CKEDITOR.dom.document( document );
 *
 * @class
 * @extends CKEDITOR.dom.domObject
 * @constructor Creates a document class instance.
 * @param {Object} domDocument A native DOM document.
 */
CKEDITOR.dom.document = function( domDocument ) {
	CKEDITOR.dom.domObject.call( this, domDocument );
};

// PACKAGER_RENAME( CKEDITOR.dom.document )

CKEDITOR.dom.document.prototype = new CKEDITOR.dom.domObject();

CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, {
	/**
	 * The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT}.
	 *
	 * @readonly
	 * @property {Number} [=CKEDITOR.NODE_DOCUMENT]
	 */
	type: CKEDITOR.NODE_DOCUMENT,

	/**
	 * Appends a CSS file to the document.
	 *
	 *		CKEDITOR.document.appendStyleSheet( '/mystyles.css' );
	 *
	 * @param {String} cssFileUrl The CSS file URL.
	 */
	appendStyleSheet: function( cssFileUrl ) {
		if ( this.$.createStyleSheet )
			this.$.createStyleSheet( cssFileUrl );
		else {
			var link = new CKEDITOR.dom.element( 'link' );
			link.setAttributes( {
				rel: 'stylesheet',
				type: 'text/css',
				href: cssFileUrl
			} );

			this.getHead().append( link );
		}
	},

	/**
	 * Creates a CSS stylesheet and inserts it into the document.
	 *
	 * @param cssStyleText {String} CSS style text.
	 * @returns {Object} The created DOM native stylesheet object.
	 */
	appendStyleText: function( cssStyleText ) {
		if ( this.$.createStyleSheet ) {
			var styleSheet = this.$.createStyleSheet( '' );
			styleSheet.cssText = cssStyleText;
		} else {
			var style = new CKEDITOR.dom.element( 'style', this );
			style.append( new CKEDITOR.dom.text( cssStyleText, this ) );
			this.getHead().append( style );
		}

		return styleSheet || style.$.sheet;
	},

	/**
	 * Creates a {@link CKEDITOR.dom.element} instance in this document.
	 *
	 * @param {String} name The name of the element.
	 * @param {Object} [attributesAndStyles]
	 * @param {Object} [attributesAndStyles.attributes] Attributes that will be set.
	 * @param {Object} [attributesAndStyles.styles] Styles that will be set.
	 * @returns {CKEDITOR.dom.element}
	 */
	createElement: function( name, attribsAndStyles ) {
		var element = new CKEDITOR.dom.element( name, this );

		if ( attribsAndStyles ) {
			if ( attribsAndStyles.attributes )
				element.setAttributes( attribsAndStyles.attributes );

			if ( attribsAndStyles.styles )
				element.setStyles( attribsAndStyles.styles );
		}

		return element;
	},

	/**
	 * Creates a {@link CKEDITOR.dom.text} instance in this document.
	 *
	 * @param {String} text Value of the text node.
	 * @returns {CKEDITOR.dom.element}
	 */
	createText: function( text ) {
		return new CKEDITOR.dom.text( text, this );
	},

	/**
	 * Moves the selection focus to this document's window.
	 */
	focus: function() {
		this.getWindow().focus();
	},

	/**
	 * Returns the element that is currently designated as the active element in the document.
	 *
	 * **Note:** Only one element can be active at a time in a document.
	 * An active element does not necessarily have focus,
	 * but an element with focus is always the active element in a document.
	 *
	 * @returns {CKEDITOR.dom.element} Active element or `null` if an IE8-9 bug is encountered.
	 * See [#10030](http://dev.ckeditor.com/ticket/10030).
	 */
	getActive: function() {
		var $active;
		try {
			$active = this.$.activeElement;
		} catch ( e ) {
			return null;
		}
		return new CKEDITOR.dom.element( $active );
	},

	/**
	 * Gets an element based on its ID.
	 *
	 *		var element = CKEDITOR.document.getById( 'myElement' );
	 *		alert( element.getId() ); // 'myElement'
	 *
	 * @param {String} elementId The element ID.
	 * @returns {CKEDITOR.dom.element} The element instance, or `null` if not found.
	 */
	getById: function( elementId ) {
		var $ = this.$.getElementById( elementId );
		return $ ? new CKEDITOR.dom.element( $ ) : null;
	},

	/**
	 * Gets a node based on its address. See {@link CKEDITOR.dom.node#getAddress}.
	 *
	 * @param {Array} address
	 * @param {Boolean} [normalized=false]
	 */
	getByAddress: function( address, normalized ) {
		var $ = this.$.documentElement;

		for ( var i = 0; $ && i < address.length; i++ ) {
			var target = address[ i ];

			if ( !normalized ) {
				$ = $.childNodes[ target ];
				continue;
			}

			var currentIndex = -1;

			for ( var j = 0; j < $.childNodes.length; j++ ) {
				var candidate = $.childNodes[ j ];

				if ( normalized === true && candidate.nodeType == 3 && candidate.previousSibling && candidate.previousSibling.nodeType == 3 )
					continue;

				currentIndex++;

				if ( currentIndex == target ) {
					$ = candidate;
					break;
				}
			}
		}

		return $ ? new CKEDITOR.dom.node( $ ) : null;
	},

	/**
	 * Gets elements list based on a given tag name.
	 *
	 * @param {String} tagName The element tag name.
	 * @returns {CKEDITOR.dom.nodeList} The nodes list.
	 */
	getElementsByTag: function( tagName, namespace ) {
		if ( !( CKEDITOR.env.ie && ( document.documentMode <= 8 ) ) && namespace )
			tagName = namespace + ':' + tagName;
		return new CKEDITOR.dom.nodeList( this.$.getElementsByTagName( tagName ) );
	},

	/**
	 * Gets the `<head>` element for this document.
	 *
	 *		var element = CKEDITOR.document.getHead();
	 *		alert( element.getName() ); // 'head'
	 *
	 * @returns {CKEDITOR.dom.element} The `<head>` element.
	 */
	getHead: function() {
		var head = this.$.getElementsByTagName( 'head' )[ 0 ];
		if ( !head )
			head = this.getDocumentElement().append( new CKEDITOR.dom.element( 'head' ), true );
		else
			head = new CKEDITOR.dom.element( head );

		return head;
	},

	/**
	 * Gets the `<body>` element for this document.
	 *
	 *		var element = CKEDITOR.document.getBody();
	 *		alert( element.getName() ); // 'body'
	 *
	 * @returns {CKEDITOR.dom.element} The `<body>` element.
	 */
	getBody: function() {
		return new CKEDITOR.dom.element( this.$.body );
	},

	/**
	 * Gets the DOM document element for this document.
	 *
	 * @returns {CKEDITOR.dom.element} The DOM document element.
	 */
	getDocumentElement: function() {
		return new CKEDITOR.dom.element( this.$.documentElement );
	},

	/**
	 * Gets the window object that stores this document.
	 *
	 * @returns {CKEDITOR.dom.window} The window object.
	 */
	getWindow: function() {
		return new CKEDITOR.dom.window( this.$.parentWindow || this.$.defaultView );
	},

	/**
	 * Defines the document content through `document.write`. Note that the
	 * previous document content will be lost (cleaned).
	 *
	 *		document.write(
	 *			'<html>' +
	 *				'<head><title>Sample Document</title></head>' +
	 *				'<body>Document content created by code.</body>' +
	 *			'</html>'
	 *		);
	 *
	 * @since 3.5
	 * @param {String} html The HTML defining the document content.
	 */
	write: function( html ) {
		// Don't leave any history log in IE. (#5657)
		this.$.open( 'text/html', 'replace' );

		// Support for custom document.domain in IE.
		//
		// The script must be appended because if placed before the
		// doctype, IE will go into quirks mode and mess with
		// the editable, e.g. by changing its default height.
		if ( CKEDITOR.env.ie )
			html = html.replace( /(?:^\s*<!DOCTYPE[^>]*?>)|^/i, '$&\n<script data-cke-temp="1">(' + CKEDITOR.tools.fixDomain + ')();</script>' );

		this.$.write( html );
		this.$.close();
	},

	/**
	 * Wrapper for `querySelectorAll`. Returns a list of elements within this document that match
	 * the specified `selector`.
	 *
	 * **Note:** The returned list is not a live collection (like the result of native `querySelectorAll`).
	 *
	 * @since 4.3
	 * @param {String} selector
	 * @returns {CKEDITOR.dom.nodeList}
	 */
	find: function( selector ) {
		return new CKEDITOR.dom.nodeList( this.$.querySelectorAll( selector ) );
	},

	/**
	 * Wrapper for `querySelector`. Returns the first element within this document that matches
	 * the specified `selector`.
	 *
	 * @since 4.3
	 * @param {String} selector
	 * @returns {CKEDITOR.dom.element}
	 */
	findOne: function( selector ) {
		var el = this.$.querySelector( selector );

		return el ? new CKEDITOR.dom.element( el ) : null;
	},

	/**
	 * Internet Explorer 8 only method. It returns a document fragment which has all HTML5 elements enabled.
	 *
	 * @since 4.3
	 * @private
	 * @returns DocumentFragment
	 */
	_getHtml5ShivFrag: function() {
		var $frag = this.getCustomData( 'html5ShivFrag' );

		if ( !$frag ) {
			$frag = this.$.createDocumentFragment();
			CKEDITOR.tools.enableHtml5Elements( $frag, true );
			this.setCustomData( 'html5ShivFrag', $frag );
		}

		return $frag;
	}
} );