/*

Siesta 5.1.0
Copyright(c) 2009-2018 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license

*/
Class('Scope.Provider.IFrame', {
    
    isa     : Scope.Provider,
    
    does    : Scope.Provider.Role.WithDOM,
    
    
    have : {
        iframe          : null,
        cls             : null,
        
        performWrap     : false,
        wrapCls         : null,
        wrapper         : null,
        
        // should be inside of the `wrapper` el
        iframeParentEl  : null,
        parentEl        : null,
        
        cleanupDelay    : 1000
    },
    

    methods : {
        
        getDocument : function () {
            return this.iframe.contentWindow.document
        },
        
        
        setViewportSize : function (width, height) {
            var iframe              = this.iframe
            
            if (!iframe) return
            
            iframe.style.width      = width + 'px'
            iframe.style.height     = height + 'px'
        },
        
        
        create : function (onLoadCallback) {
            var me                  = this
            var doc                 = this.parentWindow.document
            var iframe              = this.iframe = doc.createElement('iframe')
            
            var minViewportSize     = this.minViewportSize
            
            iframe.className        = this.cls || ''
            iframe.style.width      = (minViewportSize && minViewportSize.width || 1024) + 'px'
            iframe.style.height     = (minViewportSize && minViewportSize.height || 768) + 'px'
            iframe.setAttribute('frameborder', 0)
            
            if (this.name) iframe.setAttribute('name', this.name)

            var ignoreOnLoad        = false    
            
            var callback = function () {
                if (ignoreOnLoad) return
                
                if (iframe.detachEvent) 
                    iframe.detachEvent('onload', callback)
                else
                    iframe.onload = null
                    
                try {
                    var headContent = me.getHead().innerHTML
                    
                    onLoadCallback && onLoadCallback(me)
                } catch (e) {
                    // cross-origin exception
                    me.crossOriginFailed    = true
                    
                    onLoadCallback && onLoadCallback(me, e)
                }
            }
            
            if (iframe.attachEvent) 
                iframe.attachEvent('onload', callback)
            else
                iframe.onload   = callback
            
            iframe.src = this.sourceURL || 'about:blank'
            
            if (this.performWrap) {
                var wrapper             = this.wrapper
                
                if (!wrapper) {
                    wrapper             = this.wrapper = doc.createElement('div')
                    wrapper.className   = this.wrapCls || ''
                }
                
                ;(this.iframeParentEl || wrapper).appendChild(iframe)
                
                // no required anymore, since whole wrapper will be removed
                this.iframeParentEl     = null
            } 
            
            ;(this.parentEl || doc.body).appendChild(wrapper || iframe)
            
            var scope       = this.scope = iframe.contentWindow
            
            // dances with tambourine around the IE (probably for some old version, remove one day)
            if ('v' == '\v' || Boolean(this.parentWindow.msWriteProfilerMark)) {
                try {
                    var scopeDoc    = this.getDocument()
                    // only ignore the 1st call to callback when there is a `sourceURL` config
                    // which will later be assigned to `iframe.src` and will trigger a new iframe loading
                    if (this.sourceURL) ignoreOnLoad = true
                    
                    scopeDoc.open()
                    scopeDoc.write([
                        this.useStrictMode ? '<!DOCTYPE html>' : '',
                        '<html style="width: 100%; height: 100%; margin : 0; padding : 0;">',
                            '<head>',
                            '</head>',
                            '<body style="margin : 0; padding : 0; width: 100%; height: 100%">',
                            '</body>',
                        '</html>'
                    ].join(''))
                    scopeDoc.close()
                    
                    ignoreOnLoad = false
                } catch (e) {
                    // cross-origin exception
                    me.crossOriginFailed    = true
                }
                
                iframe.onreadystatechange = function () {
                    if (iframe.readyState == 'complete') iframe.onreadystatechange = null
                    
                    // trying to add the "early" onerror handler on each "readyState" change
                    // for some mystical reasons can't use `me.installOnErrorHandler` need to inline the call
                    if (me.cachedOnError && !me.crossOriginFailed) me.attachToOnError(scope, me.scopeId, me.cachedOnError)
                }
                
                if (this.sourceURL) iframe.src = this.sourceURL
            }
            
            // trying to add the "early" onerror handler - installing it in this stage will only work in FF 
            // (other browsers will clear on varios stages)
            if (me.cachedOnError) me.installOnErrorHandler(me.cachedOnError)
        },
        
        
        cleanup : function () {
            var wrapper     = this.wrapper || this.iframe
            var iframe      = this.iframe
            var me          = this
            
            // remove this property one more time, because sometimes it is not cleared in IE
            // (seems "onreadystatechange" is not fired)
            iframe.onreadystatechange   = null
            
            wrapper.style.display    = 'none'
            
            var onUnloadChecker = function () {
                if (!window.onunload) window.onunload = function () { return 'something' }
            }
            
            // add the `onunload` handler if there's no any - attempting to prevent browser from caching the iframe
            // trying to create the handler from inside of the scope
            this.runCode(';(' + onUnloadChecker.toString() + ')();')

            this.iframe     = null
            this.scope      = null
            this.wrapper    = null

            if (me.beforeCleanupCallback) me.beforeCleanupCallback()
            me.beforeCleanupCallback    = null
            
            if (!me.crossOriginFailed)
                // chaging the page, triggering `onunload` and hopefully preventing browser from caching the content of iframe
                iframe.src      = 'javascript:false'
            
            // wait again before removing iframe from the DOM, as recommended by some online sources
            setTimeout(function () {
                ;(me.parentEl || me.parentWindow.document.body).removeChild(wrapper)
                
                wrapper     = null
                iframe      = null
                
                me.parentEl = null
                
                me.cleanupHandlers()
                
                if (me.cleanupCallback) me.cleanupCallback()
                me.cleanupCallback  = null
                
            }, me.cleanupDelay)
        }
    }
})

/**

Name
====

Scope.Provider.IFrame - scope provider, which uses the iframe.


SYNOPSIS
========

        var provider = new Scope.Provider.IFrame()
        
        provider.setup(function () {
        
            if (provider.scope.SOME_GLOBAL == 'some_value') {
                ...
            }
            
            provider.runCode(text, callback)
            
            ...
            
            provider.runScript(url, callback)
            
            ...
            
            provider.cleanup()        
        })


DESCRIPTION
===========

`Scope.Provider.IFrame` is an implementation of the scope provider, which uses the iframe, 
to create a new scope.


ISA
===

[Scope.Provider](../Provider.html)


DOES
====

[Scope.Provider.Role.WithDOM](Role/WithDOM.html)



GETTING HELP
============

This extension is supported via github issues tracker: <http://github.com/SamuraiJack/Scope-Provider/issues>

You can also ask questions at IRC channel : [#joose](http://webchat.freenode.net/?randomnick=1&channels=joose&prompt=1)
 
Or the mailing list: <http://groups.google.com/group/joose-js>
 


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

*/