Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactoring component loading strategies, including injecting base URI.

The "nodes" and "javascript" strategies are now transformed into a "html"
strategy behind the scenes. In practice, there's no reason to ever use the
"javascript" strategy, so it's now deprecated.

The "url" strategy is the most natural strategy, and no baseURI modifications
are necessary. However, it's slower than the other strategies, and more
difficult to do content protection (if you need that).

The other two strategies, "html" and "doc", are applied to an iframe that has
been primed with "about:blank". This means it has the baseURI of its parent
document. If there are any relative hrefs to resources in the component
(scripts, stylesheets, images, etc), and the component should have a different
baseURI, these resources will ordinarily not load.

To date, Monocle has said "this is the bookData object's problem". No longer.
Now Monocle automatically injects a <base> tag into components loaded with the
"doc" or "html" strategy. You can override this by inserting a <base> tag
manually, or by listening on the reader for a monocle:component:baseuri event
and calling preventDefault() on it.
  • Loading branch information...
commit ce9be1e6711ed1f6bd97498c5ce990709c3add4c 1 parent 7418ec9
@joseph authored
View
2  src/core/bookdata.js
@@ -40,7 +40,7 @@ Monocle.bookDataFromIds = function (elementIds) {
return Monocle.bookData({
components: elementIds,
getComponent: function (cmptId) {
- return { nodes: [document.getElementById(cmptId).cloneNode(true)] }
+ return { nodes: [document.getElementById(cmptId)] }
}
});
}
View
177 src/core/component.js
@@ -42,6 +42,8 @@ Monocle.Component = function (book, id, index, chapters, source) {
// will be invoked with the pageDiv and this component as arguments.
//
function applyTo(pageDiv, callback) {
+ prepareSource(pageDiv.m.reader);
+
var evtData = { 'page': pageDiv, 'source': p.source };
pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);
@@ -74,20 +76,11 @@ Monocle.Component = function (book, id, index, chapters, source) {
// Hide the frame while we're changing it.
frame.style.visibility = "hidden";
- // Prevent about:blank overriding imported nodes in Firefox.
- // Disabled again because it seems to result in blank pages in Saf.
- //frame.contentWindow.stop();
-
- if (p.source.html || (typeof p.source == "string")) { // HTML
+ if (p.source.html) {
return loadFrameFromHTML(p.source.html || p.source, frame, callback);
- } else if (p.source.javascript) { // JAVASCRIPT
- //console.log("Loading as javascript: "+p.source.javascript);
- return loadFrameFromJavaScript(p.source.javascript, frame, callback);
- } else if (p.source.url) { // URL
+ } else if (p.source.url) {
return loadFrameFromURL(p.source.url, frame, callback);
- } else if (p.source.nodes) { // NODES
- return loadFrameFromNodes(p.source.nodes, frame, callback);
- } else if (p.source.doc) { // DOCUMENT
+ } else if (p.source.doc) {
return loadFrameFromDocument(p.source.doc, frame, callback);
}
}
@@ -119,29 +112,13 @@ Monocle.Component = function (book, id, index, chapters, source) {
}
- // LOAD STRATEGY: JAVASCRIPT
- // Like the HTML strategy, but assumes that the src string is already clean.
- //
- // DEPRECATED: HTML strategy is faster now, so this just unescapes the string
- // and loads it as HTML.
- //
- function loadFrameFromJavaScript(src, frame, callback) {
- console.deprecation("Use { 'html': src } -- no need to clean the string.");
- loadFrameFromHTML(eval("'"+src+"'"), frame, callback);
- }
-
-
// LOAD STRATEGY: URL
// Loads the URL into the given frame, invokes callback once loaded.
//
function loadFrameFromURL(url, frame, callback) {
- // If it's a relative path, we need to make it absolute, using the
- // reader's location (not the active component's location).
+ // If it's a relative path, we need to make it absolute.
if (!url.match(/^\//)) {
- var link = document.createElement('a');
- link.setAttribute('href', url);
- url = link.href;
- delete(link);
+ url = absoluteURL(url);
}
var fn = function () {
Monocle.Events.deafen(frame, 'load', fn);
@@ -152,58 +129,40 @@ Monocle.Component = function (book, id, index, chapters, source) {
}
- // LOAD STRATEGY: NODES
- // Loads the array of DOM nodes into the body of the frame (replacing all
- // existing nodes), then invokes the callback.
- //
- // DEPRECATED: now just transforms to HTML and loads as that.
- //
- function loadFrameFromNodes(nodes, frame, callback) {
- console.deprecation("Use { 'html': node.outerHTML } or similar.");
- var src = "";
- for (var i = 0; i < nodes.length; ++i) {
- src += nodes[i].outerHTML || '';
- }
- loadFrameFromHTML(src, frame, callback);
- }
-
-
// LOAD STRATEGY: DOCUMENT
// Replaces the DocumentElement of the given frame with the given srcDoc.
// Invokes the callback when loaded.
//
function loadFrameFromDocument(srcDoc, frame, callback) {
- var destDoc = frame.contentDocument;
-
- var srcBases = srcDoc.getElementsByTagName('base');
- if (srcBases[0]) {
- var head = destDoc.getElementsByTagName('head')[0];
- if (!head) {
- try {
- head = destDoc.createElement('head');
- if (destDoc.body) {
- destDoc.insertBefore(head, destDoc.body);
- } else {
- destDoc.appendChild(head);
+ var doc = frame.contentDocument;
+
+ // WebKit has an interesting quirk. The <base> tag must exist in the
+ // document being replaced, not the new document.
+ if (Monocle.Browser.is.WebKit) {
+ var srcBase = srcDoc.querySelector('base');
+ if (srcBase) {
+ var head = doc.querySelector('head');
+ if (!head) {
+ try {
+ head = doc.createElement('head');
+ prependChild(doc.documentElement, head);
+ } catch (e) {
+ head = doc.body;
}
- } catch (e) {
- head = destDoc.body;
}
+ var base = doc.createElement('base');
+ base.setAttribute('href', srcBase.href);
+ head.appendChild(base);
}
- var bases = destDoc.getElementsByTagName('base');
- var base = bases[0] ? bases[0] : destDoc.createElement('base');
- base.setAttribute('href', srcBases[0].getAttribute('href'));
- head.appendChild(base);
}
- destDoc.replaceChild(
- destDoc.importNode(srcDoc.documentElement, true),
- destDoc.documentElement
+ doc.replaceChild(
+ doc.importNode(srcDoc.documentElement, true),
+ doc.documentElement
);
- // DISABLED: immediate readiness - webkit has some difficulty with this.
- // if (callback) { callback(); }
-
+ // NB: It's a significant problem with this load strategy that there's
+ // no indication when it is complete.
Monocle.defer(callback);
}
@@ -349,6 +308,84 @@ Monocle.Component = function (book, id, index, chapters, source) {
}
+ function prepareSource(reader) {
+ if (p.sourcePrepared) { return; }
+ p.sourcePrepared = true;
+
+ if (typeof p.source == "string") {
+ p.source = { html: p.source };
+ }
+
+ // If supplied as escaped javascript, unescape it to HTML by evalling it.
+ if (p.source.javascript) {
+ console.deprecation(
+ "Loading a component by 'javascript' is deprecated. " +
+ "Use { 'html': src } -- no need to escape or clean the string."
+ );
+ p.source = { html: eval("'"+p.source.javascript+"'") };
+ }
+
+ // If supplied as DOM nodes, convert to HTML by concatenating outerHTMLs.
+ if (p.source.nodes) {
+ var srcs = [];
+ for (var i = 0, ii = p.source.nodes.length; i < ii; ++i) {
+ var node = p.source.nodes[i];
+ if (node.outerHTML) {
+ srcs.push(node.outerHTML);
+ } else {
+ var div = document.createElement('div');
+ div.appendChild(node.cloneNode(true));
+ srcs.push(div.innerHTML);
+ delete(div);
+ }
+ }
+ p.source = { html: srcs.join('') };
+ }
+
+ if (p.source.html && !p.source.html.match(new RegExp("<base\s.+>", "im"))) {
+ var baseURI = computeBaseURI(reader);
+ if (baseURI) {
+ p.source.html = p.source.html.replace(
+ new RegExp("(<head(\s[^>]*>)|>)", "im"),
+ '$1<base href="'+baseURI+'" />'
+ );
+ }
+ }
+
+ if (p.source.doc && !p.source.doc.querySelector('base')) {
+ var srcHead = p.source.doc.querySelector('head') || p.source.doc.body;
+ var baseURI = computeBaseURI(reader);
+ if (srcHead && baseURI) {
+ var srcBase = p.source.doc.createElement('base');
+ srcBase.setAttribute('href', baseURI);
+ prependChild(srcHead, srcBase);
+ }
+ }
+ }
+
+
+ function computeBaseURI(reader) {
+ var evtData = { cmptId: p.id, cmptURI: absoluteURL(p.id) }
+ if (reader.dispatchEvent('monocle:component:baseuri', evtData, true)) {
+ return evtData.cmptURI;
+ }
+ }
+
+
+ function absoluteURL(url) {
+ var link = document.createElement('a');
+ link.setAttribute('href', url);
+ result = link.href;
+ delete(link);
+ return result;
+ }
+
+
+ function prependChild(pr, el) {
+ pr.firstChild ? pr.insertBefore(el, pr.firstChild) : pr.appendChild(el);
+ }
+
+
API.applyTo = applyTo;
API.updateDimensions = updateDimensions;
API.chapterForPage = chapterForPage;
View
13 test/baseuris/cmpts/doc-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>DOC-1</title>
+ </head>
+ <body>
+ <img src="../resources/monocle.png" />
+ <p>
+ A component loaded as a document will have its baseURI injected using the
+ componentId.
+ </p>
+ </body>
+</html>
View
18 test/baseuris/cmpts/doc-2.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <!-- <script> -->
+ <!-- var rel = '/test/baseuris/cmpts/foobar/doc-2.html'; -->
+ <!-- document.write('<base href="'+location.origin+rel+'" />'); -->
+ <!-- </script> -->
+ <base href="http://monocle.dev/test/baseuris/cmpts/foobar/doc-2.html" />
+ <title>DOC-2</title>
+ </head>
+ <body>
+ <img src="../../resources/monocle.png" />
+ <p>
+ If you want to use your own baseURI, you can override it by supplying
+ a &lt;base&gt; tag in the header.
+ </p>
+ </body>
+</html>
View
14 test/baseuris/cmpts/doc-3.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>DOC-3</title>
+ </head>
+ <body>
+ <img src="resources/monocle.png" />
+ <p>
+ If you don't need baseURI injection (because all resources are referenced
+ relative to the parent document's baseURI), you can listen for and cancel
+ the <code>monocle:component:baseuri</code> event.
+ </p>
+ </body>
+</html>
View
14 test/baseuris/cmpts/doc-4.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>DOC-4</title>
+ </head>
+ <body>
+ <img src="../resources/monocle.png" />
+ <p>
+ If the component id is NOT a relative path from the parent document's
+ baseURI, you should listen for the <code>monocle:component:baseuri</code>
+ event and change the value of <code>evt.m.cmptURI</code>.
+ </p>
+ </body>
+</html>
View
13 test/baseuris/cmpts/doc-5.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>DOC-5</title>
+ </head>
+ <body>
+ <img src="resources/monocle.png" />
+ <p>
+ If the component id is just a string without forward slashes, and
+ resources are relative to the parent document's baseURI, you're all good.
+ </p>
+ </body>
+</html>
View
10 test/baseuris/cmpts/url-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>URL-1</title>
+ </head>
+ <body>
+ <img src="../resources/monocle.png" />
+ <p>A component loaded by URL has a built-in baseURI.</p>
+ </body>
+</html>
View
141 test/baseuris/index.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <title>Base URI tests</title>
+ <link rel="stylesheet" type="text/css" href="../tests.css" />
+
+ <link rel="stylesheet" type="text/css" href="../../dist/styles/monocore.css" />
+ <style type="text/css">
+ .readerInfo {
+ width: 300px;
+ float: left;
+ padding-right: 20px;
+ }
+ .reader {
+ position: relative;
+ width: 300px;
+ height: 400px;
+ border: 1px solid #000;
+ background-color: #CCC;
+ }
+
+ #hiddenFrames {
+ visibility: hidden;
+ width: 0;
+ height: 0;
+ }
+ </style>
+
+ <script type="text/javascript" src="../../dist/scripts/monocore.js"></script>
+ <script type="text/javascript">
+ Monocle.DEBUG = true;
+
+ function iframeComponent(idpart) {
+ return function () {
+ var ifr = document.getElementById('ifr_'+idpart);
+ return { doc: ifr.contentDocument };
+ }
+ }
+
+ var bookData_url_1 = Monocle.bookData({
+ components: ['cmpts/url-1.html']
+ });
+
+ var bookData_doc_1 = Monocle.bookData({
+ components: ['cmpts/doc-1.html'],
+ getComponent: iframeComponent('doc_1')
+ });
+
+ var bookData_doc_2 = Monocle.bookData({
+ components: ['cmpts/doc-2.html'],
+ getComponent: iframeComponent('doc_2')
+ });
+
+ var bookData_doc_3 = Monocle.bookData({
+ components: ['cmpts/doc-3.html'],
+ getComponent: iframeComponent('doc_3')
+ });
+
+ var bookData_doc_4 = Monocle.bookData({
+ components: ['doc-4'],
+ getComponent: iframeComponent('doc_4')
+ });
+
+ var bookData_doc_5 = Monocle.bookData({
+ components: ['doc-5'],
+ getComponent: iframeComponent('doc_5')
+ });
+
+ // Initialize the reader element.
+ Monocle.Events.listen(
+ window,
+ 'load',
+ function () {
+ window.reader1 = Monocle.Reader('reader_url_1', bookData_url_1);
+ window.reader2 = Monocle.Reader('reader_doc_1', bookData_doc_1);
+ window.reader3 = Monocle.Reader('reader_doc_2', bookData_doc_2);
+ Monocle.Events.listen(
+ document.getElementById('reader_doc_3'),
+ 'monocle:component:baseuri',
+ function (e) { e.preventDefault(); }
+ );
+ window.reader3 = Monocle.Reader('reader_doc_3', bookData_doc_3);
+ Monocle.Events.listen(
+ document.getElementById('reader_doc_4'),
+ 'monocle:component:baseuri',
+ function (e) {
+ e.m.cmptURI = 'http://monocle.dev/test/baseuris/cmpts/doc-4.html';
+ }
+ );
+ window.reader4 = Monocle.Reader('reader_doc_4', bookData_doc_4);
+ window.reader5 = Monocle.Reader('reader_doc_5', bookData_doc_5);
+ }
+ );
+ </script>
+
+ </head>
+
+ <body>
+ <a href="../index.html" class="backToGuide">&larr; Back to Guide</a>
+
+ <div class="readerInfo">
+ <h2>URL-1</h2>
+ <div class="reader" id="reader_url_1"></div>
+ </div>
+
+ <div class="readerInfo">
+ <h2>DOC-1</h2>
+ <div class="reader" id="reader_doc_1"></div>
+ </div>
+
+ <div class="readerInfo">
+ <h2>DOC-2</h2>
+ <div class="reader" id="reader_doc_2"></div>
+ </div>
+
+ <div class="readerInfo">
+ <h2>DOC-3</h2>
+ <div class="reader" id="reader_doc_3"></div>
+ </div>
+
+ <div class="readerInfo">
+ <h2>DOC-4</h2>
+ <div class="reader" id="reader_doc_4"></div>
+ </div>
+
+ <div class="readerInfo">
+ <h2>DOC-5</h2>
+ <div class="reader" id="reader_doc_5"></div>
+ </div>
+
+ <div id="hiddenFrames">
+ <iframe id="ifr_doc_1" src="cmpts/doc-1.html"></iframe>
+ <iframe id="ifr_doc_2" src="cmpts/doc-2.html"></iframe>
+ <iframe id="ifr_doc_3" src="cmpts/doc-3.html"></iframe>
+ <iframe id="ifr_doc_4" src="cmpts/doc-4.html"></iframe>
+ <iframe id="ifr_doc_5" src="cmpts/doc-5.html"></iframe>
+ </div>
+
+ </body>
+</html>
View
BIN  test/baseuris/resources/monocle.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
23 test/experimental/iframe-loading/cmpts/cmpt.css
@@ -0,0 +1,23 @@
+body {
+ margin: 0;
+ padding: 12px;
+}
+
+h1 {
+ font-size: 14pt;
+ color: green;
+}
+
+p {
+ text-indent: 1em;
+}
+
+h1, h2, h3, h4, .caption {
+ font-family: 'Belgrano', serif;
+ text-align: center;
+ display: block;
+}
+
+* {
+ max-width: 300px !important;
+}
View
381 test/experimental/iframe-loading/cmpts/cmpt.html
@@ -0,0 +1,381 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head foo="garply">
+ <title>This is a component</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="../cmpts/cmpt.css" />
+ <link
+ rel='stylesheet'
+ type='text/css'
+ href='http://fonts.googleapis.com/css?family=Belgrano'
+ />
+ </head>
+
+ <body>
+
+ <h1>This is a component</h1>
+
+ <p>Some raw unicode characters: '日本語'.</p>
+
+ <p>"Now it's our turn to show <i>you</i> some castles,
+ Greta," said Anna, as she and her
+ mother and Greta got in the car. Anna's
+ mother had promised to take the girls on an
+ all-day trip to northern Zealand. This part
+ of Denmark is as full of castles as a plum-cake
+ is full of plums.</p>
+
+ <p>After driving about twenty-five miles
+ along the shore, they came to Kronborg
+ Castle, in the town of Elsinore.</p>
+
+ <p>"This is the most famous castle in Denmark,"
+ said Anna's mother.</p>
+
+ <p>"Why?" asked Greta.</p>
+
+ <div class="figcenter" style="width: 400px;">
+ <a href="http://www.gutenberg.org/files/40647/40647-h/images/illus148.jpg"><img src="http://www.gutenberg.org/files/40647/40647-h/images/th_illus148.jpg" alt="KRONBORG CASTLE" title="KRONBORG CASTLE"></a>
+ <span class="caption">KRONBORG CASTLE</span>
+ </div>
+
+ <p>"Haven't you ever read 'Hamlet,' Greta?"
+ asked Anna in surprise. "Kronborg Castle
+ is where Hamlet lived. Only Shakespeare
+ called it Elsinore, which is the name of the
+ town instead of the castle."</p>
+
+ <p>As she walked across the old drawbridge
+ and entered the outer yard of the castle,
+ Greta thought that Kronborg was the loveliest
+ castle she had ever seen. It was much
+ larger and much more wonderful than Vosborg.
+ When they came to the drawbridge
+ across the second moat, Anna pointed out
+ the high battlement where the ghost of the
+ murdered king once walked.</p>
+
+ <p>"Does he still walk there, Anna?" asked
+ Greta.</p>
+
+ <p>"Maybe he does, Greta. All we really
+ know about him is what Shakespeare tells
+ us in his play."</p>
+
+ <p>When they crossed the last bridge and
+ passed through the last gateway into the inner
+ courtyard of Kronborg, Greta stopped
+ in surprise. "Oh, Anna, it's so large and so
+ beautiful." Then, just to show that she
+ wasn't really afraid of climbing, she asked,
+ "Tante Elsie, do you suppose we could climb
+ to the top of one of the towers?"</p>
+
+ <p>"Yes, I think so."</p>
+
+ <p>From the little balcony on the tower they
+ looked out over the ocean.</p>
+
+ <p>"Is that an island across the water?"
+ asked Greta.</p>
+
+ <p>"Oh, no, Greta. That is Sweden, only
+ three miles away. In the olden days, Kronborg
+ was a fortress that guarded the entrance
+ to the Baltic Sea. All the ships that
+ came from the North Sea into the Kattegat
+ and then into the Baltic had to pass this point
+ of land, and every ship that went by here
+ had to pay money to Denmark. Up here in
+ this very tower there were guards who
+ watched all the ships to see that every one
+ stopped and paid for the privilege of going
+ past this point. In those days, Kronborg
+ was the most important castle in Denmark."</p>
+
+ <div class="figcenter" style="width: 400px;">
+ <a href="http://www.gutenberg.org/files/40647/40647-h/images/illus151.jpg"><img src="http://www.gutenberg.org/files/40647/40647-h/images/th_illus151.jpg" alt="THE MOAT AROUND KRONBORG" title="THE MOAT AROUND KRONBORG"></a>
+ <span class="caption">THE MOAT AROUND KRONBORG</span>
+ </div>
+
+ <p>"Don't they have to pay this money now?"</p>
+
+ <p>"No. About a hundred years ago Denmark
+ stopped asking for this payment.
+ Ships that are going to the Baltic Sea can
+ now go through the Kiel Canal in Germany,
+ instead of going all the way around Denmark,"
+ explained Anna's mother.
+
+ <p>As they walked down the narrow, winding
+ stairs, Anna suddenly exclaimed, "Oh,
+ Mother, we forgot to tell Greta why Kronborg
+ is still an important castle."</p>
+
+ <p>"Maybe she already knows."</p>
+
+ <p>"No, I don't. Please tell me, Anna."</p>
+
+ <p>"According to an old, old story, Holger the
+ Dane sleeps in the dungeon that is deep below
+ this tower. If Denmark is ever in trouble
+ of any kind, he will awake and come to
+ her rescue."</p>
+
+ <p>"I've heard of Holger the Dane, of course,"
+ said Greta, "but I thought he was just an imaginary
+ person."</p>
+
+ <p>"He's no more imaginary than a Nisse,"
+ said Anna, with a twinkle in her eye.</p>
+
+ <p>Greta hated to leave Kronborg, but when
+ Anna told her that they were going to see
+ even finer castles than this one, she was willing
+ to go. After driving about fifteen miles,
+ they turned off the main road and drove
+ down a long avenue of beech trees. At the
+ end of this avenue there was a large white
+ building, with a four-cornered tower rising
+ from the center.</p>
+
+ <p>"This is Fredensborg Palace, where the
+ King and Queen live in the autumn," said
+ Anna. "The King comes here for the hunting
+ season."</p>
+
+ <p>"Can we go inside this palace?" asked
+ Greta. "I would love to see the Queen's own
+ room."</p>
+
+ <p>"Of course you would, little Margrete,"
+ said Tante Elsie. "We will ask the guide to
+ show us the Queen's apartments."</p>
+
+ <p>It seemed to Greta that the guide took
+ them through miles and miles of rooms.
+ Even then, he showed them only a part of
+ the two hundred and seventy rooms. The
+ palace was much larger than it looked from
+ the front, for it was very long.</p>
+
+ <p>"Surely this isn't the <i>Queen's</i> room," said
+ Greta, as she stood in the doorway of the
+ large, sunny bedroom.</p>
+
+ <p>"Why not, Greta?" asked Anna in surprise.</p>
+
+ <p>"Why, I thought it would be a very grand
+ room, with furniture of gold."</p>
+
+ <p>Tante Elsie laughed. "Oh, Greta, you
+ forget that the King and Queen of Denmark
+ are people of simple tastes. This is a beautiful
+ room, and it shows that the Queen likes
+ lovely things. But it also shows that she
+ does not spend money just to make a grand
+ display."</p>
+
+ <p>"Perhaps the young lady would like to see
+ the ballroom," said the guide. "I think she
+ will find that it is all that she imagined and
+ really fit for a queen."</p>
+
+ <div class="figcenter" style="width: 400px;">
+ <a href="http://www.gutenberg.org/files/40647/40647-h/images/illus155.jpg"><img src="http://www.gutenberg.org/files/40647/40647-h/images/th_illus155.jpg" alt="FREDERIKSBORG CASTLE" title="FREDERIKSBORG CASTLE"></a>
+ <span class="caption">FREDERIKSBORG CASTLE</span>
+ </div>
+
+ <p>He led them through several drawing-rooms
+ and then into the ballroom. It was a
+ large, square room, with windows clear up
+ to the ceiling. The walls and ceiling were
+ light blue, so that it seemed as if the room
+ were open to the sky.</p>
+
+ <p>"This is what I always thought a royal
+ palace would be like," said Greta in deep contentment.</p>
+
+ <p>"The ceiling of this room is eighty-one
+ feet high," explained the guide. "It forms
+ the tower in the middle of the palace."</p>
+
+ <p>Greta didn't want to leave this lovely room.
+ She was fascinated by the pictures painted
+ on the walls, the gorgeous hangings at the
+ windows, and the large groups of glass
+ candlesticks that hung from the ceiling and
+ sparkled like icicles as the sun shone on
+ them.</p>
+
+ <p>"Do you want to see the Queen's crown?"
+ asked Anna, as they reluctantly left the
+ beautiful ballroom.</p>
+
+ <p>"The Queen's crown?" said Greta. "The
+ Queen is in Copenhagen now; surely her
+ crown wouldn't be here at Fredensborg."
+
+ <p>"She has a crown here, too. It is really
+ lovelier than the one she wears at court,"
+ said Anna.</p>
+
+ <p>When they came to the garden behind the
+ palace, Greta saw what Anna meant. In the
+ middle of the sloping green lawn there was
+ a huge bed of pansies, arranged in the shape
+ of a crown. In the lower part of the crown
+ the pansies were all purple, and in the upper
+ part they were all gold.</p>
+
+ <p>"You are right, Anna. This is prettier
+ than any crown of real gold."</p>
+
+ <p>"Come, girls. We must be on our way
+ now," said Anna's mother. "We have a long
+ ride ahead of us."</p>
+
+ <p>"Are we going to see <i>another</i> castle?"
+ asked Greta.</p>
+
+ <p>"Yes, the finest one of all."</p>
+
+ <div class="figcenter" style="width: 400px;">
+ <a href="http://www.gutenberg.org/files/40647/40647-h/images/illus158.jpg"><img src="http://www.gutenberg.org/files/40647/40647-h/images/th_illus158.jpg" alt="THE COURTYARD OF FREDERIKSBORG" title="THE COURTYARD OF FREDERIKSBORG"></a>
+ <span class="caption">THE COURTYARD OF FREDERIKSBORG</span>
+ </div>
+
+ <p>After a short drive through a large forest
+ of beech trees, they came to Frederiksborg
+ Castle. It was built on three small islands in
+ the center of a lake. The castle itself was so
+ large that it covered the islands completely
+ and seemed to rise up out of the water itself.
+ When King Frederick built the first castle
+ here, nearly four hundred years ago, he purposely
+ chose these islands in the center of a
+ lake because they were the safest place for a
+ castle. Enemies could not easily reach it
+ here. When this old, old castle was destroyed
+ by fire, a much finer one was built on
+ the islands.</p>
+
+ <div class="figcenter" style="width: 400px;">
+ <a href="http://www.gutenberg.org/files/40647/40647-h/images/illus159.jpg"><img src="http://www.gutenberg.org/files/40647/40647-h/images/th_illus159.jpg" alt="FREDENSBORG PALACE" title="FREDENSBORG PALACE"></a>
+ <span class="caption">FREDENSBORG PALACE</span>
+ </div>
+
+ <p>As they crossed the bridge to the main
+ part of the castle, Greta saw two women entering
+ the main doorway.</p>
+
+ <p>"Look, Anna," she said in great excitement.
+ "Isn't that Princess Ingrid and Princess
+ Louise going into the castle right this
+ minute?"</p>
+
+ <p>When Anna looked, the women had disappeared.
+ Greta was now more eager than
+ ever to visit the castle. Maybe she would
+ see her beloved Princess Ingrid again.
+ Would the Princess remember the little girl
+ who had found her handkerchief on the stairs
+ of the Marble Church? Greta wondered.
+ She was anxious to go inside, but Anna
+ stopped in the courtyard to listen to the
+ chimes on the castle tower. Every hour of
+ the day the ringing chimes played a different
+ tune.</p>
+
+ <p>"Let's see the Knights' Hall first," suggested
+ Anna, when they went inside.</p>
+
+ <p>Greta wanted to say, "Let's follow Princess
+ Ingrid," but she didn't know which way
+ the Princess had gone. So she followed
+ Anna without a word. The Knights' Hall
+ was the largest room Greta had ever seen.
+ The walls were covered with hangings whose
+ colored threads were woven in such a way as
+ to show scenes from Danish history. Greta
+ thought she would like to study history if
+ she could learn it from pictures like these instead
+ of from textbooks. While she was
+ looking at one of the hangings in a far corner
+ of the room, Anna grabbed her arm.</p>
+
+ <p>"There they go, Greta. Don't you want to
+ follow them?"</p>
+
+ <p>Then Greta remembered that Princess
+ Ingrid was here in the castle. How could she
+ have forgotten? She and Anna and Tante
+ Elsie left the Knights' Hall and entered the
+ long series of drawing-rooms that filled one
+ wing of Frederiksborg. Greta scarcely
+ looked at the paintings that hung on every
+ wall. She wanted to see a real live princess,
+ not a painted lady in a golden frame. They
+ walked slowly through room after room, but
+ not a glimpse of the Princess did they get.
+
+ <p>"I guess Princess Ingrid and her sister
+ have left the castle," said Greta, in an unhappy
+ tone. She was ready now to leave it
+ herself, for it held no further interest for
+ her.</p>
+
+ <p>"Now for the most wonderful room of all,"
+ said Anna, after they had gone through all
+ the drawing-rooms. She almost had to drag
+ Greta to the chapel, which formed an important
+ part of the castle itself. When the
+ royal family lived at Frederiksborg, services
+ were held here every Sunday. This chapel
+ was really as large as a church. As they
+ came in, the organist was playing softly, and
+ Greta sat down to listen to the music, while
+ Anna and her mother went to look at the
+ paintings which were on exhibition in the
+ balcony of the chapel.</p>
+
+ <p>Afterwards, Greta never remembered exactly
+ how it happened. She must have closed
+ her eyes for a minute in order to enjoy the
+ music completely. The first thing she remembered
+ was a soft voice saying, "Are you
+ all alone here, my dear?"</p>
+
+ <p>When Greta had opened her eyes and recognized
+ the speaker she jumped to her feet.
+ "Why, it's the little girl who found my handkerchief
+ the other day. How very nice to see
+ you again. When we saw you here asleep,
+ we thought you had gotten lost in this great
+ castle."</p>
+
+ <p>Greta explained that her aunt and her
+ cousin were up in the balcony. Then her eyes
+ shone like stars and her heart almost skipped
+ a beat as the lovely Princess Ingrid took a
+ rosebud from the bouquet which she carried
+ and held it out to Greta. "Perhaps you
+ would like to wear this little flower," she said
+ in her gentle way. Then, with another smile,
+ she and the Princess Louise went out of the
+ chapel.</p>
+
+ <p>Greta stood like one in a dream. Now she
+ knew how the knights of olden times felt
+ when they had been honored by the King.
+ She, little Margrete, had a flower from the
+ hands of the Crown Princess of Denmark.
+ All her life long she would remember this
+ moment.</p>
+
+ <hr style="width: 65%;">
+
+ </body>
+</html>
View
70 test/experimental/iframe-loading/index.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html id="topdoc">
+ <head>
+ <title>Iframe load tests</title>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="../../tests.css" />
+
+ <style>
+ #reader {
+ border: 1px dotted #F0F;
+ margin: 20px 0;
+ width: 330px;
+ height: 400px;
+ position: relative;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ overflow: hidden;
+ }
+ #reader iframe {
+ width: 100%;
+ height: 100%;
+ border: none;
+ }
+ select {
+ max-width: 35%;
+ }
+ #loadHiddenFrame {
+ width: 0;
+ height: 0;
+ visibility: hidden;
+ }
+ </style>
+
+ <script src="loader.js"></script>
+ </head>
+
+ <body class="narrow">
+
+ <a href="../../index.html" class="backToGuide">&larr; Back to Guide</a>
+ <h1>Interaction Model test</h1>
+
+ <div id="reader">
+ <iframe id="loadFrame" src="about:blank"></iframe>
+ </div>
+
+ <p>
+ <label>Load with:
+ <select id="loadWith">
+ <option value="url">Normal URL</option>
+ <option value="jsurl">JavaScript URL</option>
+ <option value="escapedjsurl">Escaped JavaScript URL</option>
+ <option value="docWrite">document.write</option>
+ <option value="dataurienc">Data URI (URI-enc)</option>
+ <option value="iframedocument">Import from iframe</option>
+ </select>
+ </label>
+ <input id="loadGo" type="button" value="Load!" />
+ <input id="loadReset" type="button" value="Reset" />
+ </p>
+
+ <pre id="loadResults"></pre>
+
+ <hr />
+
+ <iframe id="loadHiddenFrame" src="cmpts/cmpt.html"></iframe>
+
+ </body>
+</html>
View
172 test/experimental/iframe-loading/loader.js
@@ -0,0 +1,172 @@
+var IframeLoader = {};
+
+
+IframeLoader.get = function (path) {
+ var ajReq = new XMLHttpRequest();
+ ajReq.open("GET", path, false);
+ ajReq.send(null);
+ return ajReq.responseText;
+}
+
+
+IframeLoader.init = function () {
+ IframeLoader.testIframeWindow();
+ IframeLoader.url = 'cmpts/cmpt.html';
+ IframeLoader.str = IframeLoader.get(IframeLoader.url);
+ IframeLoader.frame = document.getElementById('loadFrame');
+ IframeLoader.frame.onload = IframeLoader.doneLoad;
+ IframeLoader.results = document.getElementById('loadResults')
+ document.getElementById('loadGo').onclick = IframeLoader.doLoad;
+ document.getElementById('loadReset').onclick = IframeLoader.doReset;
+}
+
+
+IframeLoader.setUpFrame = function () {
+ IframeLoader.frame.onload = IframeLoader.doneLoad;
+}
+
+
+IframeLoader.doLoad = function () {
+ var sel = document.getElementById('loadWith');
+ IframeLoader.results.innerHTML += sel.value+': ';
+ IframeLoader.tStart = (new Date()).getTime();
+ IframeLoader['load_'+sel.value]();
+}
+
+
+IframeLoader.doneLoad = function () {
+ var msecs = (new Date()).getTime() - IframeLoader.tStart;
+ IframeLoader.results.innerHTML += (msecs / 1000.0)+"s";
+ try {
+ var b = IframeLoader.frame.contentDocument.body.innerHTML.length;
+ IframeLoader.results.innerHTML += " - "+b+"b\n";
+ } catch (e) {
+ IframeLoader.results.innerHTML += " - INACCESSIBLE\n";
+ }
+}
+
+
+IframeLoader.doReset = function () {
+ IframeLoader.frame.onload = IframeLoader.setUpFrame;
+ IframeLoader.frame.src = 'about:blank';
+ IframeLoader.results.innerHTML += "-------\n";
+}
+
+
+IframeLoader.injectBaseURI = function (str) {
+ var a = document.createElement('a');
+ a.setAttribute('href', IframeLoader.url);
+ var baseURI = a.href;
+ var headStartTagPattern = new RegExp("(<head(\s[^>]*>)|>)", "im");
+ return str.replace(headStartTagPattern, '$1<base href="'+baseURI+'" />');
+}
+
+
+/* LOADER FUNCTIONS */
+
+IframeLoader.load_url = function () {
+ IframeLoader.frame.src = IframeLoader.injectBaseURI(IframeLoader.url);
+}
+
+
+IframeLoader.load_jsurl = function () {
+ var src = IframeLoader.injectBaseURI(IframeLoader.str);
+ IframeLoader.frame.contentWindow['cmpt'] = src;
+ IframeLoader.frame.src = 'javascript:window["cmpt"];';
+}
+
+
+IframeLoader.load_docWrite = function () {
+ var src = IframeLoader.injectBaseURI(IframeLoader.str);
+ var doc = IframeLoader.frame.contentWindow.document;
+ doc.open('text/html', 'replace');
+ doc.write(src);
+ doc.close();
+}
+
+
+IframeLoader.load_escapedjsurl = function () {
+ var src = IframeLoader.injectBaseURI(IframeLoader.str);
+ src = src.replace(/\n/g, '\\n');
+ src = src.replace(/\r/, '\\r');
+ src = src.replace(/\'/g, '\\\'');
+ IframeLoader.frame.src = "javascript:'"+src+"'";
+}
+
+
+IframeLoader.load_dataurienc = function () {
+ var src = IframeLoader.injectBaseURI(IframeLoader.str);
+ src = encodeURIComponent(src);
+ IframeLoader.frame.src = 'data:text/html;charset=utf-8,'+src;
+}
+
+
+IframeLoader.load_iframedocument = function () {
+ var srcDoc = document.getElementById('loadHiddenFrame').contentDocument;
+ var doc = IframeLoader.frame.contentDocument;
+
+ // Replicate the base element the current contentDocument for WebKit.
+
+ var srcBase = srcDoc.querySelector('base');
+ if (!srcBase) {
+ var a = document.createElement('a');
+ a.setAttribute('href', IframeLoader.url);
+ var baseURI = a.href;
+
+ srcBase = srcDoc.createElement('base');
+ srcBase.setAttribute('href', baseURI);
+ var srcHead = srcDoc.querySelector('head');
+ srcHead.insertBefore(srcBase, srcHead.firstChild);
+ }
+
+ if (navigator.userAgent.match(/Apple\s?WebKit/)) {
+ var head = doc.querySelector('head');
+ if (!head) {
+ try {
+ head = doc.createElement('head');
+ doc.body ? doc.insertBefore(head, doc.body) : doc.appendChild(head);
+ } catch (e) {
+ head = doc.body;
+ }
+ }
+ head.appendChild(doc.importNode(srcBase, false));
+ }
+
+ // Import the document.
+ doc.replaceChild(
+ doc.importNode(srcDoc.documentElement, true),
+ doc.documentElement
+ );
+
+ setTimeout(IframeLoader.doneLoad, 0);
+}
+
+
+/* A LITTLE BIT OF IFRAME BEHAVIOURAL TESTING */
+
+IframeLoader.testIframeWindow = function () {
+ var ifr = document.createElement('iframe');
+ try {
+ ifr.contentWindow.innerWidth;
+ } catch(e) {
+ console.log('Failed to access content window before adding to DOM.');
+ }
+ document.body.appendChild(ifr);
+ try {
+ ifr.contentWindow.innerWidth;
+ } catch(e) {
+ console.log('Failed to access content window before assigning src.');
+ }
+ ifr.src = 'about:blank';
+ try {
+ ifr.contentWindow.innerWidth;
+ } catch(e) {
+ console.log('Failed to access content window after assigning src.');
+ }
+ document.body.removeChild(ifr);
+}
+
+
+/* INIT */
+
+window.onload = IframeLoader.init;
View
4 test/index.html
@@ -130,6 +130,10 @@
&mdash; Some old mobile browsers struggle with iframes inside iframes.
</li>
<li>
+ <a href="baseuris/index.html">Base URIs</a>
+ &mdash; Loading relative-URL images/stylesheets etc in components.
+ </li>
+ <li>
<a href="bugs/">Client-specific bug index</a>
&mdash; A repository of other bugs that afflict only specific clients.
</li>
Please sign in to comment.
Something went wrong with that request. Please try again.