siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
610 lines (415 loc) • 21.5 kB
HTML
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js">/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
Role('Scope.Provider.Role.WithDOM', {
requires : [ 'getDocument', 'create', 'getPreload', 'isAlreadySetUp', 'setViewportSize' ],
has : {
useStrictMode : true,
sourceURL : null,
crossOriginFailed : false,
innerHtmlHead : null,
innerHtmlBody : null,
minViewportSize : null,
parentWindow : function () { return window },
scopeId : function () { return Math.round(Math.random() * 1e10) },
failOnResourceLoadError : false,
failOnPromiseRejection : true,
// init function
attachToOnError : function () {
// returns the value of the attribute
// the "handler" argument is no longer used, its now being taken from the __ONERROR__ handler every time
return function (window, scopeId, handler, preventDefault, failOnResourceLoadError, failOnPromiseRejection) {
handler = (window.opener || window.parent).Scope.Provider.__ONERROR__[ scopeId ]
if (failOnResourceLoadError && ("ErrorEvent" in window)) {
// if (window.ErrorEvent.__SIESTA_HOOK_INSTALLED__) return
// http://stackoverflow.com/questions/8504673/how-to-detect-on-page-404-errors-using-javascript
window.addEventListener('error', handler, true)
// window.ErrorEvent.__SIESTA_HOOK_INSTALLED__ = true
} else {
var prevHandler = window.onerror
if (prevHandler && prevHandler.__SP_MANAGED__) return
// this, "managed" handler is basically a wrapper around the current value in the "__ONERROR__" hash
window.onerror = function (message, url, lineNumber) {
// prevent recursive calls if other authors politely has not overwrite the handler and call it
if (handler.__CALLING__) return
handler.__CALLING__ = true
prevHandler && prevHandler.apply(this, arguments)
handler.apply(this, arguments)
handler.__CALLING__ = false
// in FF/IE need to return `true` to prevent default action
if (preventDefault !== false) return window.WebKitPoint ? false : true
}
window.onerror.__SP_MANAGED__ = true
}
if (failOnPromiseRejection && window.Promise && ('onunhandledrejection' in window)) {
window.addEventListener('unhandledrejection', function (e) {
handler.apply(this, [ null, null, null, null, null, e ])
}, true)
}
}
},
// this is a "cached" onerror handler - a handler which was provided before the scope
// has started the creation process - should be installed ASAP in the creation process
// to allow catching of the exceptions in the scope with `sourceURL`
cachedOnError : null
},
override : {
cleanup : function () {
var onErrorHandler = this.cachedOnError
this.cachedOnError = null
// can throw exceptions for cross-domain case
try {
var scope = this.scope
if (scope.ErrorEvent /*&& scope.ErrorEvent.__SIESTA_HOOK_INSTALLED__*/) scope.removeEventListener('error', onErrorHandler)
scope.onerror = null
} catch (e) {
}
this.SUPERARG(arguments)
this.scope = null
}
},
methods : {
cleanupHandlers : function () {
var scopeProvider = this.parentWindow.Scope.Provider
var scopeId = this.scopeId
delete scopeProvider.__ONLOAD__[ scopeId ]
delete scopeProvider.__ONERROR__[ scopeId ]
delete scopeProvider.__FAILED_PRELOAD__[ scopeId ]
},
getHead : function () {
return this.getDocument().getElementsByTagName('head')[ 0 ]
},
installOnErrorHandler : function (handler) {
if (this.crossOriginFailed) return
if (!this.isAlreadySetUp()) throw "Scope should be already set up"
this.attachToOnError(this.scope, this.scopeId, handler, false, this.failOnResourceLoadError, this.failOnPromiseRejection)
},
addOnErrorHandler : function (handler, preventDefault) {
if (this.crossOriginFailed) return
handler.__SP_MANAGED__ = true
this.cachedOnError = handler
var scopeId = this.scopeId
this.parentWindow.Scope.Provider.__ONERROR__[ scopeId ] = handler
var attachToOnError = ';(' + this.attachToOnError.toString() + ')(window, '
+ scopeId
+ ', (window.opener || window.parent).Scope.Provider.__ONERROR__[ ' + scopeId + ' ], '
+ preventDefault + ', '
+ this.failOnResourceLoadError + ', '
+ this.failOnPromiseRejection
+ ');'
if (this.isAlreadySetUp())
this.runCode(attachToOnError)
else {
// this is a fallback - run the "attachToOnError" from inside of scope
this.getPreload().unshift({
type : 'js',
content : attachToOnError,
unordered : true
})
}
},
addSeedingToPreload : function () {
var preload = this.getPreload()
if (this.seedingCode) preload.unshift({
type : 'js',
content : this.seedingCode
})
if (this.seedingScript) preload.push({
type : 'js',
url : this.seedingScript,
isEcmaModule : this.seedingScriptIsEcmaModule
})
},
setup : function (callback) {
var isIE = 'v' == '\v' || Boolean(this.parentWindow.msWriteProfilerMark)
// var isOpera = Object.prototype.toString.call(this.parentWindow.opera) == '[object Opera]'
var hasInlineScript = false
Joose.A.each(this.getPreload(), function (preloadDesc) {
// IE will execute the inline scripts ASAP, this might be not what we want (inline script might be need executed only after some url script)
// its however ok in some cases (like adding `onerror` handler
// such inline scripts should be marked with `unordered` - true
if (preloadDesc.type == 'js' && preloadDesc.content && !preloadDesc.unordered) {
hasInlineScript = true
return false
}
})
var me = this
var cont = function (e) {
callback && callback(me, me.parentWindow.Scope.Provider.__FAILED_PRELOAD__[ me.scopeId ], e)
}
this.parentWindow.Scope.Provider.__FAILED_PRELOAD__[ this.scopeId ] = {}
if (this.sourceURL || isIE && hasInlineScript) {
this.addSeedingToPreload()
this.setupIncrementally(cont)
} else {
// for sane browsers just add the seeding code and seeding script to preloads
if (!isIE) this.addSeedingToPreload()
// seeding scripts are included only for sane browsers (not IE)
this.setupWithDocWrite(cont, isIE)
}
},
setupWithDocWrite : function (callback, needToSeed) {
var html = []
var me = this
Joose.A.each(this.getPreload(), function (preloadDesc) {
if (preloadDesc.type == 'js')
html.push(
me.getScriptTagString(preloadDesc.url, preloadDesc.content, preloadDesc.isEcmaModule)
)
else if (preloadDesc.type == 'css')
html.push(
me.getLinkTagString(preloadDesc.url, preloadDesc.content)
)
else throw "Incorrect preload descriptor " + preloadDesc
})
// no need to wait for DOM ready - we'll overwrite it anyway
this.create()
var scopeId = this.scopeId
this.parentWindow.Scope.Provider.__ONLOAD__[ scopeId ] = function () {
var cont = function () { callback && callback() }
// sane browsers - seeding code and script has been already added
if (!needToSeed) { cont(); return }
// our beloved IE - manually seeding the scope
if (me.seedingCode) me.runCode(me.seedingCode, null)
if (me.seedingScript)
me.runScript(me.seedingScript, cont, me.seedingScriptIsEcmaModule)
else
cont()
}
var doc = this.getDocument()
doc.open()
doc.write([
this.useStrictMode ? '<!DOCTYPE html>' : '',
'<html style="width: 100%; height: 100%; margin : 0; padding : 0;">',
'<head>',
this.innerHtmlHead || '',
html.join(''),
'</head>',
// delay here is for IE9 - the "onerror" handlers of the <script> tags are fired _after_ <body> onload otherwise
'<body style="margin : 0; padding : 0; width: 100%; height: 100%" onload="setTimeout(function () { (window.opener || window.parent).Scope.Provider.__ONLOAD__[' + scopeId + ']() }, 0)">',
this.innerHtmlBody || '',
'</body>',
'</html>'
].join(''))
doc.close()
// Chrome (Webkit?) will clear the `onerror` after "doc.open()/.close()" so need to re-install it
if (me.cachedOnError) me.installOnErrorHandler(me.cachedOnError)
},
setupIncrementally : function (callback) {
// here the "onerror" should be included early in the "preloads"
this.create(function (me, e) {
if (e) {
callback && callback(e)
return
}
if (!me.sourceURL) {
var doc = me.getDocument()
if (me.innerHtmlHead) {
var head = me.getHead()
// IE9 throws exception when accessing innerHTML of the <head> - its read only
try {
head.innerHTML = me.innerHtmlHead
} catch (e) {
var div = doc.createElement('div')
div.innerHTML = me.innerHtmlHead
for (var i = 0; i < div.children.length; i++) head.appendChild(div.children[ i ])
}
}
if (me.innerHtmlBody) doc.body.innerHTML = me.innerHtmlBody
}
var loadScripts = function (preloads, callback) {
var cont = function () {
// cleanup can happen in the middle of setup
if (me.scope) loadScripts(preloads, callback)
}
if (!preloads.length)
callback && callback()
else {
var preloadDesc = preloads.shift()
if (preloadDesc.url)
me.runScript(preloadDesc.url, cont, preloadDesc.isEcmaModule)
else
if (preloadDesc.type == 'js')
me.runCode(preloadDesc.content, cont, preloadDesc.isEcmaModule)
else {
me.addStyleTag(preloadDesc.content)
cont()
}
}
}
// cleanup can happen in the middle of setup
if (me.scope) loadScripts(me.getPreload().slice(), callback)
})
},
getScriptTagString : function (url, text, isEcmaModule) {
var type = isEcmaModule ? 'module' : 'text/javascript',
res = '<script type="' + type + '"' + (isEcmaModule ? ' crossorigin="anonymous"' : '')
var onerror = '(window.opener || window.parent).Scope.Provider.__FAILED_PRELOAD__[ scopeId ][ url ] = true'
onerror = onerror.replace(/scopeId/, "'" + this.scopeId + "'").replace(/url/, "'" + url + "'")
if (url)
res += ' src="' + url + '" onerror="' + onerror + '"></script>'
else
res += '>' + text.replace(/<\/script>/gi, '\\x3C/script>') + '</script>'
return res
},
getLinkTagString : function (url, text) {
if (url) return '<link href="' + url + '" rel="stylesheet" type="text/css" />'
if (text) return '<style>' + text + '</style>'
},
loadCSS : function (url, callback) {
var doc = this.getDocument()
var link = doc.createElement('link')
link.type = 'text/css'
link.rel = 'stylesheet'
link.href = url
var hasOnLoad = false
if ('onload' in link) {
hasOnLoad = true
link.onload = function () {
link.onload = null
if (callback) callback()
callback = null
link = null
}
}
this.getHead().appendChild(link)
if (!hasOnLoad) {
var hasContinued = false
var cont = function () {
// just in case some crazy JS engine calls `onerror` even after node removal
if (hasContinued) return
hasContinued = true
clearTimeout(forcedTimeout)
if (callback) callback()
doc.body.removeChild(img)
}
var forcedTimeout = setTimeout(cont, 30000)
var img = doc.createElement('img')
img.onerror = cont
doc.body.appendChild(img)
img.src = url
}
},
runCode : function (text, callback, isEcmaModule) {
if (this.crossOriginFailed) {
callback && callback()
return
}
this.getHead().appendChild(this.createScriptTag(text, null, null, null, isEcmaModule))
callback && callback()
},
runScript : function (url, callback, isEcmaModule) {
if (this.crossOriginFailed) {
callback && callback()
return
}
var scopeId = this.scopeId
if (this.isCSS(url))
this.loadCSS(url, callback)
else {
var onerror = function () {
this.onerror = null
var doc = this.ownerDocument
var win = doc.defaultView || doc.parentWindow
;(win.opener || win.parent).Scope.Provider.__FAILED_PRELOAD__[ scopeId ][ url ] = true
callback()
}
this.getHead().appendChild(this.createScriptTag(null, url, callback, onerror, isEcmaModule))
}
},
createScriptTag : function (text, url, callback, errback, isEcmaModule) {
var node = this.getDocument().createElement("script")
node.setAttribute("type", isEcmaModule ? 'module' : 'text/javascript')
isEcmaModule && node.setAttribute("crossorigin", "anonymous")
if (url) node.setAttribute("src", url)
if (text) node.text = text
if (callback)
node.onload = node.onreadystatechange = function () {
if (!node.readyState || node.readyState == "loaded" || node.readyState == "complete" || node.readyState == 4 && node.status == 200) {
node.onload = node.onreadystatechange = null
//surely for IE6..
if ('v' == '\v')
setTimeout(callback, 0)
else
callback()
}
}
if (errback) node.onerror = errback
return node
},
addStyleTag : function (text) {
var document = this.getDocument()
var node = document.createElement('style')
node.setAttribute("type", "text/css")
var head = document.getElementsByTagName('head')[0]
head.appendChild(node)
if (node.styleSheet) { // IE
node.styleSheet.cssText = text
} else { // the world
node.appendChild(document.createTextNode(text))
}
}
}
})
<span id='global-property-'>/**
</span>
Name
====
Scope.Provider.Role.WithDOM - role for scope provider, which uses `script` tag for running the code.
SYNOPSIS
========
Class('Scope.Provider.IFrame', {
isa : Scope.Provider,
does : Scope.Provider.Role.WithDOM,
...
})
DESCRIPTION
===========
`Scope.Provider.Role.WithDOM` requires the implementation of the `getDocument` method, which should return the
document into which the `script` tags will be created.
In return, this role provides the implementation of `runCode` and `runScript`.
GETTING HELP
============
This extension is supported via github issues tracker: <http://github.com/SamuraiJack/Scope-Provider/issues>
For general Joose questions you can also visit [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
on irc.freenode.org or the forum at: <http://joose.it/forum>
SEE ALSO
========
Web page of this module: <http://github.com/SamuraiJack/Scope-Provider/>
General documentation for Joose: <http://joose.github.com/Joose>
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at <http://github.com/SamuraiJack/Scope-Provider/issues>
AUTHORS
=======
Nickolay Platonov <nplatonov@cpan.org>
COPYRIGHT AND LICENSE
=====================
This software is Copyright (c) 2010 by Nickolay Platonov <nplatonov@cpan.org>.
This is free software, licensed under:
The GNU Lesser General Public License, Version 3, June 2007
*/
</pre>
</body>
</html>