<commit_before>// is the captured node exempt from fragment loading?
function fragmentFilter(el) {
    var href = el.getAttribute('href') || el.getAttribute('action'),
        $el = $(el);
    return !href || href.substr(0,4) == 'http' ||
            href.substr(0,7) === 'mailto:' ||
            href.substr(0,11) === 'javascript:' ||
            href.substr(0,1) === '#' ||
            href.indexOf('/developers/') !== -1 ||
            href.indexOf('/ecosystem/') !== -1 ||
            href.indexOf('/statistics/') !== -1 ||
            href.indexOf('?modified=') !== -1 ||
            el.getAttribute('target') === '_blank' ||
            el.getAttribute('rel') === 'external' ||
            $el.hasClass('post') || $el.hasClass('sync');
}

(function(container, nodeFilter) {
    var threshold = 250,
        timeout = false,
        fragmentCache = {},
        cacheHash = '',
        reloadOnNext = false;

    if (z.capabilities.replaceState) {
        var $loading = $('
', {'class': 'loading-fragment overlay',
                                   'html': ''})
                        .prependTo($('body'));

        z.doc.on('reloadonnext', function() {
            reloadOnNext = true;
        });

        // Hijack <form> submission.
        z.body.on('submit', 'form', function(e) {
            if (reloadOnNext) return;
            var form = $(this);
            var method = form.attr('method').toLowerCase();
            if (method === 'get') {
                e.preventDefault();
                hijackGET(form);
            } else if (method === 'post') {
                e.preventDefault();
                hijackPOST(form);
            }
            // Not GET or POST? Not interested.
        });

        z.doc.ajaxSuccess(function(e, xhr, response) {
            // Do we have an instruction to clear a part of the fragment cache?
            var bust_flag = xhr.getResponseHeader('x-frag-bust');
            if (bust_flag) {

                // The `fcbust` header is a JSON-encoded list of strings.
                var decoded_prefixes = JSON.parse(bust_flag);

                for (var i = 0; i < decoded_prefixes.length; i++) {
                    var clear_prefix = decoded_prefixes[i];

                    // Delete all the matching cache entries.
                    _.each(fragmentCache, function(_, key) {
                        // If the prefix doesn't match the cache entry, skip it.
                        if (clear_prefix.length > key.length ||
                            key.substr(0, clear_prefix.length) !== clear_prefix) {
                            return;
                        }

                        delete fragmentCache[key];
                    });
                };
                console.log('fragment cache busted');
            }
        });

        function handleFragmentResponse(xhr, href) {
            var bust_flag = xhr.getResponseHeader('x-frag-bust');
            if (bust_flag && _.contains(JSON.parse(bust_flag), '/')) {
                synchronousLoad(xhr.getResponseHeader('x-uri') || href);
            }
        }

        function handleOffline() {
            console.log('user is offline');
            localStorage.from = window.location.href;
            window.location = '/offline/home';
        }

        function hijackGET(form) {
            var action = form.attr('action');
            //strip existing queryparams off the action.
            var link = document.createElement('a');
            link.href = action;
            var path = link.pathname + '?' + form.serialize();
            form.trigger('loadfragment', path);
        }

        function hijackPOST(form) {
            console.log('hijacking POST');
            startLoading();
            var action = form.attr('action') || window.location.href;
            var data = form.serialize();
            if (data) {
                data += '&_hijacked=true';
            } else {
                data = '_hijacked=true';
            }
            var href = action || window.location;
            $.ajax({
                type: 'POST',
                url: href,
                data: data
            }).done(function(response, textStatus, xhr) {
                // load up the response fragment!
                handleFragmentResponse(xhr, href);
                updateContent(response);
            }).fail(function(response) {
                if (navigator.onLine === false) {
                    handleOffline();
                    return;
                }
                form.trigger('notify', {
                    title: gettext('Error'),
                    msg: gettext('A server error has occurred.')
                });
                endLoading();
            });
        }

        // capture clicks in our target environment
        z.body.on('click', 'a', function(e) {
            if (reloadOnNext) return;
            var href = this.getAttribute('href');
            if (e.metaKey || e.ctrlKey || e.button !== 0) return;
            if (nodeFilter(this)) {
                return;
            }
            e.preventDefault();
            navigate({path: href});
        });

        // preserve scrollTop like a real boy
        function markScrollTop() {
            var path = window.location.pathname + window.location.search + window.location.hash;
            var state = {path: path, scrollTop: $(document).scrollTop()};
            console.log(format('setting the scrolltop for {0}', path));
            history.replaceState(state, false, path);
        }

        // start the loading indicator
        function startLoading() {
            container.trigger('startfragmentload');
            timeout = setTimeout(function() {
                $loading.addClass('show');
            }, threshold);
            loadTimer = new Date().getTime();
        }

        // end the loading timer and report timing
        function endLoading() {
            clearTimeout(timeout);
            _.delay(function() {
                $loading.removeClass('show');
            }, 400);
            stick.custom({
                'window.performance.timing.fragment.loaded':
                    (new Date()).getTime() - loadTimer
            });
        }

        // handle link clicking and state popping.
        function navigate(state, popped) {
            var href = state.path;
            startLoading();
            markScrollTop();
            getFragment(href).done(function(content) {
                updateContent(content, href, popped, {scrollTop: state.scrollTop});
            }).fail(function() {
                console.log('fetch error!');
                if (navigator.onLine === false) {
                    handleOffline();
                    return;
                }
                z.doc.trigger('notify', {
                    title: gettext('Error'),
                    msg: gettext('There was an error loading the requested page.')});
            }).always(endLoading);
        }

        function getFragment(href) {
            var $def = $.Deferred();
            //caching!
            if (fragmentCache[href]) {
                console.log(format('cached {0}', href));
                $def.resolve(fragmentCache[href]);
            } else {
                console.log(format('fetching {0}', href));
                fetchFragment(href).done(function(d, textStatus, xhr) {
                    // Bail if this is not HTML.
                    if (xhr.getResponseHeader('content-type').indexOf('text/html') < 0) {
                        $def.reject();
                        return;
                    }
                    if (d.indexOf('') === -1) {
                        console.log('warning, fragment not properly served!');
                        return;
                    }
                    console.log('header is ' + xhr.getResponseHeader('date'));
                    console.log('caching fragment');
                    handleFragmentResponse(xhr, href);
                    $def.resolve(d);
                }).fail(function(e) {
                    $def.reject();
                });
            }
            return $def.promise();
        }

        function fetchFragment(href) {
            // Chrome doesn't obey the Vary header, so we need to keep the fragments
            // from getting cached with the page itself. Remove once Chrome bug #94369
            // has been resolved as fixed.
            var fetch_href = href;
            if (navigator.userAgent.indexOf('Chrome') > -1) {
                var chr_flag = '';

                // If we have a locale/region, we want to include those in the request
                // so Chrome does a hard reload of those pages when they change.
                var lang = $.cookie('lang');
                if (lang) {
                    chr_flag += lang;
                }
                var region = $.cookie('region');
                if (region) {
                    chr_flag += region;
                }

                if(chr_flag) {
                    chr_flag = '=' + encodeURIComponent(chr_flag);
                }

                fetch_href += (href.indexOf("?") > -1 ? '&' : '?') + 'frag' + chr_flag;
            }

            return $.get(fetch_href);
        }

        function synchronousLoad(href) {
            // TODO(potch): Save the user's navigation stack.
            window.location.href = href;
        }

        // pump content into our page, clean up after ourselves.
        function updateContent(content, href, popped, opts) {
            opts = opts || {};
            endLoading();

            container.html(content);
            var page = container.find('#page');
            if (!page.length) {
                throw "something has gone terribly wrong";
            }

            // Reset our lovelies.
            _.extend(z, {
                body: $(document.body),
                page: $('#container'),
                context: $('#page').data('context')
            });

            if (z.context.type === 'offline') {
                console.log('oh no!');
                return;
            }

            if (!href) {
                href = z.context.uri;
                console.log('whats the 411' + href);
            }

            if (z.context.cache === 'cache') {
                console.log('caching fragment');
                fragmentCache[href] = content;
            }

            // If we have new media, load the next fragment synchronously.
            if (z.context.hash != cacheHash) {
                reloadOnNext = true;
                container.trigger('fragmentpendingsync');
                console.log('performing synchronous load next navigation');
            }

            // Clear jQuery's data attribute cache for body.
            jQuery.cache[document.body[jQuery.expando]].data = null;

            // We so sneaky.
            document.title = z.context.title;

            var type = z.context.type;

            var newState = {
                path: href,
                type: type,
                title: z.context.title,
                scrollTop: $(document).scrollTop()
            };
            if (!popped) history.pushState(newState, false, href);

            // Scroll to the requested spot.
            $('html, body').scrollTop(opts.scrollTop || 0);

            container.trigger('fragmentloaded', [href, popped, newState]);
        }

        z.win.on('popstate', function(e) {
            var state = e.originalEvent.state;
            if (state) {
                navigate(state, true);
            }
        });
        z.doc.on('loadfragment', function(e, href) {
            if (href) navigate({path: href});
        }).on('refreshfragment', function(e) {
            var path = window.location.pathname + window.location.search + window.location.hash;
            if (fragmentCache[path]) {
                delete fragmentCache[path];
            }
            navigate({path: path});
        }).on('updatecache', function() {
            console.log('event');
            updateCache();
        });

        function updateCache() {
            console.log('caching fragment');
            fragmentCache[window.location.pathname] = container.html();
        }

        $(function() {
            // Don't forget to update updateContent too, bro.
            var path = window.location.pathname + window.location.search + window.location.hash;
            var type = z.context.type;
            cacheHash = z.context.hash;
            var state = {
                path: path,
                type: type
            };
            history.replaceState(state, false, path);

            if (z.context.cache === 'cache') {
                updateCache();
            }
            container.trigger('fragmentloaded', [path, false, state]);
        });
        console.log("fragments enabled");
    } else {
        console.warn("fragments not enabled!!");
        $(function() {
            container.trigger('fragmentloaded');
        });
    }
})(z.page, fragmentFilter);
<commit_msg>temporary console.log debug statements (bug 855510)<commit_after>// is the captured node exempt from fragment loading?
function fragmentFilter(el) {
    var href = el.getAttribute('href') || el.getAttribute('action'),
        $el = $(el);
    return !href || href.substr(0,4) == 'http' ||
            href.substr(0,7) === 'mailto:' ||
            href.substr(0,11) === 'javascript:' ||
            href.substr(0,1) === '#' ||
            href.indexOf('/developers/') !== -1 ||
            href.indexOf('/ecosystem/') !== -1 ||
            href.indexOf('/statistics/') !== -1 ||
            href.indexOf('?modified=') !== -1 ||
            el.getAttribute('target') === '_blank' ||
            el.getAttribute('rel') === 'external' ||
            $el.hasClass('post') || $el.hasClass('sync');
}

(function(container, nodeFilter) {
    var threshold = 250,
        timeout = false,
        fragmentCache = {},
        cacheHash = '',
        reloadOnNext = false;

    if (z.capabilities.replaceState) {
        var $loading = $('
', {'class': 'loading-fragment overlay',
                                   'html': ''})
                        .prependTo($('body'));

        z.doc.on('reloadonnext', function() {
            reloadOnNext = true;
        });

        // Hijack <form> submission.
        z.body.on('submit', 'form', function(e) {
            if (reloadOnNext) return;
            var form = $(this);
            var method = form.attr('method').toLowerCase();
            if (method === 'get') {
                e.preventDefault();
                hijackGET(form);
            } else if (method === 'post') {
                e.preventDefault();
                hijackPOST(form);
            }
            // Not GET or POST? Not interested.
        });

        z.doc.ajaxSuccess(function(e, xhr, response) {
            // Do we have an instruction to clear a part of the fragment cache?
            var bust_flag = xhr.getResponseHeader('x-frag-bust');
            if (bust_flag) {

                // The `fcbust` header is a JSON-encoded list of strings.
                var decoded_prefixes = JSON.parse(bust_flag);

                for (var i = 0; i < decoded_prefixes.length; i++) {
                    var clear_prefix = decoded_prefixes[i];

                    // Delete all the matching cache entries.
                    _.each(fragmentCache, function(_, key) {
                        // If the prefix doesn't match the cache entry, skip it.
                        if (clear_prefix.length > key.length ||
                            key.substr(0, clear_prefix.length) !== clear_prefix) {
                            return;
                        }

                        delete fragmentCache[key];
                    });
                };
                console.log('fragment cache busted');
            }
        });

        function handleFragmentResponse(xhr, href) {
            var bust_flag = xhr.getResponseHeader('x-frag-bust');
            if (bust_flag && _.contains(JSON.parse(bust_flag), '/')) {
                synchronousLoad(xhr.getResponseHeader('x-uri') || href);
            }
        }

        function handleOffline() {
            console.log('user is offline');
            localStorage.from = window.location.href;
            window.location = '/offline/home';
        }

        function hijackGET(form) {
            var action = form.attr('action');
            //strip existing queryparams off the action.
            var link = document.createElement('a');
            link.href = action;
            var path = link.pathname + '?' + form.serialize();
            form.trigger('loadfragment', path);
        }

        function hijackPOST(form) {
            console.log('hijacking POST');
            startLoading();
            var action = form.attr('action') || window.location.href;
            var data = form.serialize();
            if (data) {
                data += '&_hijacked=true';
            } else {
                data = '_hijacked=true';
            }
            var href = action || window.location;
            $.ajax({
                type: 'POST',
                url: href,
                data: data
            }).done(function(response, textStatus, xhr) {
                // load up the response fragment!
                handleFragmentResponse(xhr, href);
                updateContent(response);
            }).fail(function(response) {
                if (navigator.onLine === false) {
                    handleOffline();
                    return;
                }
                form.trigger('notify', {
                    title: gettext('Error'),
                    msg: gettext('A server error has occurred.')
                });
                endLoading();
            });
        }

        // capture clicks in our target environment
        z.body.on('click', 'a', function(e) {
            if (reloadOnNext) return;
            var href = this.getAttribute('href');
            if (e.metaKey || e.ctrlKey || e.button !== 0) return;
            if (nodeFilter(this)) {
                return;
            }
            e.preventDefault();
            navigate({path: href});
        });

        // preserve scrollTop like a real boy
        function markScrollTop() {
            var path = window.location.pathname + window.location.search + window.location.hash;
            var state = {path: path, scrollTop: $(document).scrollTop()};
            console.log(format('setting the scrolltop for {0}', path));
            history.replaceState(state, false, path);
        }

        // start the loading indicator
        function startLoading() {
            container.trigger('startfragmentload');
            timeout = setTimeout(function() {
                $loading.addClass('show');
            }, threshold);
            loadTimer = new Date().getTime();
        }

        // end the loading timer and report timing
        function endLoading() {
            clearTimeout(timeout);
            _.delay(function() {
                $loading.removeClass('show');
            }, 400);
            stick.custom({
                'window.performance.timing.fragment.loaded':
                    (new Date()).getTime() - loadTimer
            });
        }

        // handle link clicking and state popping.
        function navigate(state, popped) {
            var href = state.path;
            startLoading();
            markScrollTop();
            getFragment(href).done(function(content) {
                updateContent(content, href, popped, {scrollTop: state.scrollTop});
            }).fail(function() {
                console.log('fetch error!');
                if (navigator.onLine === false) {
                    handleOffline();
                    return;
                }
                z.doc.trigger('notify', {
                    title: gettext('Error'),
                    msg: gettext('There was an error loading the requested page.')});
            }).always(endLoading);
        }

        function getFragment(href) {
            var $def = $.Deferred();
            //caching!
            if (fragmentCache[href]) {
                console.log(format('cached {0}', href));
                $def.resolve(fragmentCache[href]);
            } else {
                console.log(format('fetching {0}', href));

                // TEMP!
                console.log(format('window.location.href: {0}', window.location.href));
                console.log(format('document.cookie: {0}', document.cookie));

                fetchFragment(href).done(function(d, textStatus, xhr) {
                    // Bail if this is not HTML.
                    if (xhr.getResponseHeader('content-type').indexOf('text/html') < 0) {
                        $def.reject();
                        return;
                    }
                    if (d.indexOf('') === -1) {
                        console.log('warning, fragment not properly served!');
                        return;
                    }
                    console.log('header is ' + xhr.getResponseHeader('date'));
                    console.log('caching fragment');
                    handleFragmentResponse(xhr, href);
                    $def.resolve(d);
                }).fail(function(e) {
                    $def.reject();
                });
            }
            return $def.promise();
        }

        function fetchFragment(href) {
            // Chrome doesn't obey the Vary header, so we need to keep the fragments
            // from getting cached with the page itself. Remove once Chrome bug #94369
            // has been resolved as fixed.
            var fetch_href = href;
            if (navigator.userAgent.indexOf('Chrome') > -1) {
                var chr_flag = '';

                // If we have a locale/region, we want to include those in the request
                // so Chrome does a hard reload of those pages when they change.
                var lang = $.cookie('lang');
                if (lang) {
                    chr_flag += lang;
                }
                var region = $.cookie('region');
                if (region) {
                    chr_flag += region;
                }

                if(chr_flag) {
                    chr_flag = '=' + encodeURIComponent(chr_flag);
                }

                fetch_href += (href.indexOf("?") > -1 ? '&' : '?') + 'frag' + chr_flag;
            }

            return $.get(fetch_href);
        }

        function synchronousLoad(href) {
            // TODO(potch): Save the user's navigation stack.
            window.location.href = href;
        }

        // pump content into our page, clean up after ourselves.
        function updateContent(content, href, popped, opts) {
            opts = opts || {};
            endLoading();

            container.html(content);
            var page = container.find('#page');
            if (!page.length) {
                throw "something has gone terribly wrong";
            }

            // Reset our lovelies.
            _.extend(z, {
                body: $(document.body),
                page: $('#container'),
                context: $('#page').data('context')
            });

            if (z.context.type === 'offline') {
                console.log('oh no!');
                return;
            }

            if (!href) {
                href = z.context.uri;
                console.log('whats the 411' + href);
            }

            if (z.context.cache === 'cache') {
                console.log('caching fragment');
                fragmentCache[href] = content;
            }

            // If we have new media, load the next fragment synchronously.
            if (z.context.hash != cacheHash) {
                reloadOnNext = true;
                container.trigger('fragmentpendingsync');
                console.log('performing synchronous load next navigation');
            }

            // Clear jQuery's data attribute cache for body.
            jQuery.cache[document.body[jQuery.expando]].data = null;

            // We so sneaky.
            document.title = z.context.title;

            var type = z.context.type;

            var newState = {
                path: href,
                type: type,
                title: z.context.title,
                scrollTop: $(document).scrollTop()
            };
            if (!popped) history.pushState(newState, false, href);

            // Scroll to the requested spot.
            $('html, body').scrollTop(opts.scrollTop || 0);

            container.trigger('fragmentloaded', [href, popped, newState]);
        }

        z.win.on('popstate', function(e) {
            var state = e.originalEvent.state;
            if (state) {
                navigate(state, true);
            }
        });
        z.doc.on('loadfragment', function(e, href) {
            if (href) navigate({path: href});
        }).on('refreshfragment', function(e) {
            var path = window.location.pathname + window.location.search + window.location.hash;
            if (fragmentCache[path]) {
                delete fragmentCache[path];
            }
            navigate({path: path});
        }).on('updatecache', function() {
            console.log('event');
            updateCache();
        });

        function updateCache() {
            console.log('caching fragment');
            fragmentCache[window.location.pathname] = container.html();
        }

        $(function() {
            // Don't forget to update updateContent too, bro.
            var path = window.location.pathname + window.location.search + window.location.hash;
            var type = z.context.type;
            cacheHash = z.context.hash;
            var state = {
                path: path,
                type: type
            };
            history.replaceState(state, false, path);

            if (z.context.cache === 'cache') {
                updateCache();
            }
            container.trigger('fragmentloaded', [path, false, state]);
        });
        console.log("fragments enabled");
    } else {
        console.warn("fragments not enabled!!");
        $(function() {
            container.trigger('fragmentloaded');
        });
    }
})(z.page, fragmentFilter);
<|endoftext|>


<commit_before>WebVRApplication = ( function () {
    function WebVRApplication(scene, config) {
        this.scene = scene;

        config = config || {};
        var rendererOptions     = config.rendererOptions || {antialias: true, alpha: true};
        var useShadowMap        = config.useShadowMap;
        var onResetVRSensor     = config.onResetVRSensor;
        var useWebVRBoilerplate = config.useWebVRBoilerplate;

        var world = config.world;
        if (!world) {
            var worldConfig = config.worldConfig || {};
            worldConfig.gravity                    = worldConfig.gravity || 9.8;
            worldConfig.contactEquationStiffness   = worldConfig.contactEquationStiffness || 1e7;
            worldConfig.frictionEquationStiffness  = worldConfig.frictionEquationStiffness || 1e7;
            worldConfig.contactEquationRelaxation  = worldConfig.contactEquationRelaxation || 3;
            worldConfig.frictionEquationRelaxation = worldConfig.frictionEquationRelaxation || 3;
            worldConfig.iterations                 = worldConfig.iterations || 8;

            world = new CANNON.World();
            world.gravity.set( 0, -worldConfig.gravity, 0 );
            world.broadphase = new CANNON.SAPBroadphase( world );
            world.defaultContactMaterial.contactEquationStiffness   = worldConfig.contactEquationStiffness;
            world.defaultContactMaterial.frictionEquationStiffness  = worldConfig.frictionEquationStiffness;
            world.defaultContactMaterial.contactEquationRelaxation  = worldConfig.contactEquationRelaxation;
            world.defaultContactMaterial.frictionEquationRelaxation = worldConfig.frictionEquationRelaxation;
            world.solver.iterations = worldConfig.iterations;
        }
        this.world = world;

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        this.camera = camera;

        var renderer = new THREE.WebGLRenderer(rendererOptions);
        this.renderer = renderer;
        this.renderer.setPixelRatio(window.devicePixelRatio);
        if (useShadowMap) {
            this.renderer.shadowMap.enabled = true;
            this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        }
        var domElement = this.renderer.domElement;
        document.body.appendChild(domElement);
        domElement.id = 'renderer';

        var vrEffect = new THREE.VREffect(this.renderer);
        this.vrEffect = vrEffect;
        this.vrEffect.setSize(window.innerWidth, window.innerHeight);

        this.vrControls = new THREE.VRControls(this.camera);
        this.vrControls.enabled = true;

        if (useWebVRBoilerplate) {

            this.vrManager = new WebVRManager(this.renderer, this.vrEffect, {
                hideButton: false
            });

            this.enterVR = function () {
            };

        } else {

            var vrMode = 0;

            window.addEventListener("resize", function () {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                vrEffect.setSize(window.innerWidth, window.innerHeight);
            });

            this.enterVR = function () {
                if (vrMode === 0) {
                    this.vrEffect.setFullScreen(true);
                }
            }.bind(this);

            this.vrManager = ( function () {
                var onFullscreenChange = function () {
                    if (vrMode === 0) {
                        vrMode = 1;
                        vrEffect.setSize(window.innerWidth, window.innerHeight);
                    } else {
                        vrMode = 0;
                    }
                };
                document.addEventListener('webkitfullscreenchange', onFullscreenChange);
                document.addEventListener('mozfullscreenchange', onFullscreenChange);
                // window.addEventListener('keydown', function (evt) {
                //     if (evt.keyCode === 70) { // F
                //         this.enterVR();
                //     }
                // });
                return {
                    isVRMode: function () {
                        return vrMode === 1;
                    },
                    render: function (scene, camera, t) {
                        if (vrMode === 1) vrEffect.render(scene, camera);
                        else renderer.render(scene, camera);
                    }
                };
            }.bind(this) )();

            this.enterFullscreen = function () {
                // Primrose function:
                function requestFullScreen ( elem, vrDisplay ) {
                    var fullScreenParam;
                    if ( window.HMDVRDevice && vrDisplay && vrDisplay instanceof HMDVRDevice) {
                        fullScreenParam = {vrDisplay: vrDisplay};
                    }
                    if ( elem.webkitRequestFullscreen && fullScreenParam ) {
                        elem.webkitRequestFullscreen( fullScreenParam );
                    }
                    else if ( elem.webkitRequestFullscreen && !fullScreenParam ) {
                        elem.webkitRequestFullscreen( window.Element.ALLOW_KEYBOARD_INPUT );
                    }
                    else if ( elem.mozRequestFullScreen && fullScreenParam ) {
                        elem.mozRequestFullScreen( fullScreenParam );
                    }
                    else if ( elem.mozRequestFullScreen && !fullScreenParam ) {
                        elem.mozRequestFullScreen( );
                    }
                    else if ( elem.requestFullscreen ) {
                        elem.requestFullscreen();
                    }
                    else if ( elem.msRequestFullscreen ) {
                        elem.msRequestFullscreen();
                    }
                }
                requestFullScreen(domElement);
            };

        }

        this.toggleVRControls = function () {
            if (this.vrControls.enabled) {
                this.vrControls.enabled = false;
                this.camera.position.set(0, 0, 0);
                this.camera.quaternion.set(0, 0, 0, 1);
            } else {
                this.vrControls.enabled = true;
            }
        }.bind(this);

        var lastPosition = new THREE.Vector3();
        this.resetVRSensor = function () {
            lastPosition.copy(this.camera.position);
            var lastRotation = this.camera.rotation.y;
            this.vrControls.resetSensor();
            this.vrControls.update();
            if (onResetVRSensor) {
                onResetVRSensor(lastRotation, lastPosition);
            }
        }.bind(this);

        var wireframeMaterial = new THREE.MeshBasicMaterial({color: 0xeeddaa, wireframe: true});
        this.toggleWireframe = function () {
            if (this.scene.overrideMaterial) {
                this.scene.overrideMaterial = null;
            } else {
                this.scene.overrideMaterial = wireframeMaterial;
            }
        }.bind(this);

        renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock || renderer.domElement.mozRequestPointerLock || renderer.domElement.webkitRequestPointerLock;
        function requestPointerLock() {
            if (renderer.domElement.requestPointerLock) {
                renderer.domElement.requestPointerLock();
            }
        }
        function releasePointerLock() {
            document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
            if (document.exitPointerLock) {
                document.exitPointerLock();
            }
        }
        var fullscreenchange = this.renderer.domElement.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
        document.addEventListener(fullscreenchange, function ( event ) {
            if (this.vrManager.isVRMode()) {
                this.vrControls.enabled = true;
            }
            var fullscreen = !(document.webkitFullscreenElement === null || document.mozFullScreenElement === null);
            if (!fullscreen) {
                releasePointerLock();
            } else {
                requestPointerLock();
            }
        }.bind(this));

        this.start = function(animate) {
            function waitForResources(t) {
                if (THREE.py.isLoaded()) {
                    THREE.py.CANNONize(scene, world);
                    for (var i = 0; i < 240*2; i++) {
                        world.step(1/240);
                    }
                    requestAnimationFrame(animate);
                } else {
                    requestAnimationFrame(waitForResources);
                }
            }
            waitForResources(0);
        };

    }

    return WebVRApplication;
} )();
<commit_msg>fullscreen<commit_after>WebVRApplication = ( function () {
    function WebVRApplication(scene, config) {
        this.scene = scene;

        config = config || {};
        var rendererOptions     = config.rendererOptions || {antialias: true, alpha: true};
        var useShadowMap        = config.useShadowMap;
        var onResetVRSensor     = config.onResetVRSensor;
        var useWebVRBoilerplate = config.useWebVRBoilerplate;

        var world = config.world;
        if (!world) {
            var worldConfig = config.worldConfig || {};
            worldConfig.gravity                    = worldConfig.gravity || 9.8;
            worldConfig.contactEquationStiffness   = worldConfig.contactEquationStiffness || 1e7;
            worldConfig.frictionEquationStiffness  = worldConfig.frictionEquationStiffness || 1e7;
            worldConfig.contactEquationRelaxation  = worldConfig.contactEquationRelaxation || 3;
            worldConfig.frictionEquationRelaxation = worldConfig.frictionEquationRelaxation || 3;
            worldConfig.iterations                 = worldConfig.iterations || 8;

            world = new CANNON.World();
            world.gravity.set( 0, -worldConfig.gravity, 0 );
            world.broadphase = new CANNON.SAPBroadphase( world );
            world.defaultContactMaterial.contactEquationStiffness   = worldConfig.contactEquationStiffness;
            world.defaultContactMaterial.frictionEquationStiffness  = worldConfig.frictionEquationStiffness;
            world.defaultContactMaterial.contactEquationRelaxation  = worldConfig.contactEquationRelaxation;
            world.defaultContactMaterial.frictionEquationRelaxation = worldConfig.frictionEquationRelaxation;
            world.solver.iterations = worldConfig.iterations;
        }
        this.world = world;

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        this.camera = camera;

        var renderer = new THREE.WebGLRenderer(rendererOptions);
        this.renderer = renderer;
        this.renderer.setPixelRatio(window.devicePixelRatio);
        if (useShadowMap) {
            this.renderer.shadowMap.enabled = true;
            this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        }
        var domElement = this.renderer.domElement;
        document.body.appendChild(domElement);
        domElement.id = 'renderer';

        var vrEffect = new THREE.VREffect(this.renderer);
        this.vrEffect = vrEffect;
        this.vrEffect.setSize(window.innerWidth, window.innerHeight);

        this.vrControls = new THREE.VRControls(this.camera);
        this.vrControls.enabled = true;

        if (useWebVRBoilerplate) {

            this.vrManager = new WebVRManager(this.renderer, this.vrEffect, {
                hideButton: false
            });

            this.enterVR = function () {
            };

        } else {

            var vrMode = 0;

            window.addEventListener("resize", function () {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                vrEffect.setSize(window.innerWidth, window.innerHeight);
            });

            this.enterVR = function () {
                if (vrMode === 0) {
                    this.vrEffect.setFullScreen(true);
                }
            }.bind(this);

            this.vrManager = ( function () {
                var onFullscreenChange = function () {
                    if (vrMode === 0) {
                        vrMode = 1;
                        vrEffect.setSize(window.innerWidth, window.innerHeight);
                    } else {
                        vrMode = 0;
                    }
                };
                document.addEventListener('webkitfullscreenchange', onFullscreenChange);
                document.addEventListener('mozfullscreenchange', onFullscreenChange);
                // window.addEventListener('keydown', function (evt) {
                //     if (evt.keyCode === 70) { // F
                //         this.enterVR();
                //     }
                // });
                return {
                    isVRMode: function () {
                        return vrMode === 1;
                    },
                    render: function (scene, camera, t) {
                        if (vrMode === 1) vrEffect.render(scene, camera);
                        else renderer.render(scene, camera);
                    }
                };
            }.bind(this) )();

            this.enterFullscreen = function () {
                // Primrose function:
                function requestFullScreen ( elem, vrDisplay ) {
                    var fullScreenParam;
                    // if ( window.HMDVRDevice && vrDisplay && vrDisplay instanceof HMDVRDevice) {
                    //     fullScreenParam = {vrDisplay: vrDisplay};
                    // }
                    if ( elem.webkitRequestFullscreen && fullScreenParam ) {
                        elem.webkitRequestFullscreen( fullScreenParam );
                    }
                    else if ( elem.webkitRequestFullscreen && !fullScreenParam ) {
                        elem.webkitRequestFullscreen( window.Element.ALLOW_KEYBOARD_INPUT );
                    }
                    else if ( elem.mozRequestFullScreen && fullScreenParam ) {
                        elem.mozRequestFullScreen( fullScreenParam );
                    }
                    else if ( elem.mozRequestFullScreen && !fullScreenParam ) {
                        elem.mozRequestFullScreen( );
                    }
                    else if ( elem.requestFullscreen ) {
                        elem.requestFullscreen();
                    }
                    else if ( elem.msRequestFullscreen ) {
                        elem.msRequestFullscreen();
                    }
                }
                requestFullScreen(domElement);
            };

        }

        this.toggleVRControls = function () {
            if (this.vrControls.enabled) {
                this.vrControls.enabled = false;
                this.camera.position.set(0, 0, 0);
                this.camera.quaternion.set(0, 0, 0, 1);
            } else {
                this.vrControls.enabled = true;
            }
        }.bind(this);

        var lastPosition = new THREE.Vector3();
        this.resetVRSensor = function () {
            lastPosition.copy(this.camera.position);
            var lastRotation = this.camera.rotation.y;
            this.vrControls.resetSensor();
            this.vrControls.update();
            if (onResetVRSensor) {
                onResetVRSensor(lastRotation, lastPosition);
            }
        }.bind(this);

        var wireframeMaterial = new THREE.MeshBasicMaterial({color: 0xeeddaa, wireframe: true});
        this.toggleWireframe = function () {
            if (this.scene.overrideMaterial) {
                this.scene.overrideMaterial = null;
            } else {
                this.scene.overrideMaterial = wireframeMaterial;
            }
        }.bind(this);

        renderer.domElement.requestPointerLock = renderer.domElement.requestPointerLock || renderer.domElement.mozRequestPointerLock || renderer.domElement.webkitRequestPointerLock;
        function requestPointerLock() {
            if (renderer.domElement.requestPointerLock) {
                renderer.domElement.requestPointerLock();
            }
        }
        function releasePointerLock() {
            document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
            if (document.exitPointerLock) {
                document.exitPointerLock();
            }
        }
        var fullscreenchange = this.renderer.domElement.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
        document.addEventListener(fullscreenchange, function ( event ) {
            if (this.vrManager.isVRMode()) {
                this.vrControls.enabled = true;
            }
            var fullscreen = !(document.webkitFullscreenElement === null || document.mozFullScreenElement === null);
            if (!fullscreen) {
                releasePointerLock();
            } else {
                requestPointerLock();
            }
        }.bind(this));

        this.start = function(animate) {
            function waitForResources(t) {
                if (THREE.py.isLoaded()) {
                    THREE.py.CANNONize(scene, world);
                    for (var i = 0; i < 240*2; i++) {
                        world.step(1/240);
                    }
                    requestAnimationFrame(animate);
                } else {
                    requestAnimationFrame(waitForResources);
                }
            }
            waitForResources(0);
        };

    }

    return WebVRApplication;
} )();
<|endoftext|>


<commit_before>/**
@fileOverview Boom interface for wysihtml5.
*/
/**
* Interface for the wysihtml5 editor.
* @class
* @name $.wysihtml5.editor
* @extends $.boom.editor
*/
$.widget('wysihtml5.editor', $.boom.textEditor,
	/** @lends $.wysihtml5.editor */
	{
	/**
	@property mode
	@type string
	*/
	mode : 'block',

	/**
	@property options
	@type object
	*/
	options : {
	},

	/**
	* @function
	@returns {Deferred}
	*/
	load : function() {
		this._load_extensions(wysihtml5);
	},

	/**
	* @function
	@param {Object} element The element being edited.
	@returns {Deferred}
	*/
	edit : function (element) {
		var self = this,
			element;
console.log(element.html());
console.log(wysihtml5ParserRules);
		self.mode = element.is('div') ? 'block' : 'inline';
		self.mode = (element.is(':header') ||  element.is('.standFirst'))? 'text' : self.mode;
		self.edited = new $.Deferred();
		self.original_html = element.html();

		self._insert_toolbar(element)
			.done(function() {
				self.instance = new wysihtml5.Editor(element[0], { // id of textarea element
					toolbar : 'wysihtml5-toolbar',
					style : true,
					parserRules :  wysihtml5ParserRules, // defined in parser rules set
					useLineBreaks : false,
					contentEditableMode : true
				});

				$('#wysihtml5-toolbar')
					.on('click', '#b-editor-link', function(e) {
						e.preventDefault();
						var href = $( '[data-wysihtml5-dialog-field=href]' ).val();
						var match = href.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );
						var asset_id = match ? match[2] : 0;
						if (asset_id == 0) {
							self._edit_link();
						} else {
							self._edit_asset(asset_id);
							resizeIframe();
						}
					})
					.on('click', '#b-editor-asset', function(e) {
						e.preventDefault();
						var src = $( '[data-wysihtml5-dialog-field=src]' ).val();
						var asset_id = 0;
						if ( src && src != 'http://' ) {
							var match = src.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );

							asset_id = match ? match[2] : 0;
						}

						self._edit_asset(asset_id);
					})
					.on('click', '#b-editor-accept', function(event) {
						event.preventDefault();
						self.apply(element);
						return false;
					})
					.on( 'click', '#b-editor-cancel', function( event ){
						event.preventDefault();
						self.cancel(element);
						return false;
					});

				self.instance.on( 'show:dialog', function( options ){
					switch( options.command ) {
						case 'createLink' :
							var href = $( '[data-wysihtml5-dialog-field=href]' ).val();

							if ( ! href || href == 'http://') {
								self._edit_link();
							}
							break;
						case 'insertImage' :
							var src = $( '[data-wysihtml5-dialog-field=src]' ).val();
							var asset_id = 0;
							if ( src && src != 'http://' ) {
								var match = src.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );

								asset_id = match ? match[2] : 0;
							}

							if ( ! asset_id) {
								self._edit_asset(asset_id);
							}
							break;
					}
				});
			});

		return self.edited;

	},

	/**
	* @function
	*/
	remove : function(element) {
		$('#wysihtml5-toolbar').remove();
		element.blur();
		element.removeAttr('contenteditable');

		this.instance = null;
	},

	/**
	* @function
	@param {Object} element The element being edited.
	*/
	apply : function(element) {
		element.blur();
		element.removeAttr('contenteditable');
		this.edited.resolve(element.html());

		this.remove(element);
	},

	/**
	* @function
	@param {Object} element The element being edited.
	*/
	cancel : function(element) {
		var self = this,
			content = element.html();

		if (content != self.original_html) {
			$.boom.dialog.confirm('Cancel changes', 'Cancel all changes and exit the editor?')
				.done(function() {
					$.boom.log( 'canceling text edits' );

					self.remove(element);

					self.edited.reject();
				});
		} else {
			self.remove(element);
			self.edited.reject();
		}
	},

	/**
	@function
	@param {Object} element The element being edited.
	@returns {Deferred}
	*/
	_insert_toolbar : function(element) {
		var self = this;

		 return $.get('/media/boom/toolbars/text.php?mode=' + self.mode)
			.done(function(response) {
				$('body').prepend(response)
			});
	},

	/**
	@function
	@returns {Deferred}
	*/
	_edit_asset : function(asset_rid) {
		var self = this,
			ed = self.instance.composer,
			asset_selected = new $.Deferred(),
			img;

		if (asset_rid == 0) {
			ed.commands.exec("insertHTML", '');
			img = $(ed.element).find( '[src=url]' );
			if ( ! img.length)
				img = $(ed.element).find( '[href=url]' );
		} else {
			img = $(ed.element).find( '[src^="/asset/view/' + asset_rid +'"]' );
			if ( ! img.length)
				img = $(ed.element).find( '[href^="/asset/view/' + asset_rid +'"]' );
		}

		// cleanup code when the dialog closes.
		asset_selected
		.fail( function() {
			$( ed.element ).find( '[src=url]' ).remove() || $( ed.element ).find( '[href=url]' ).remove();
		});

		return $.boom.assets
			.picker({
				asset_rid : asset_rid,
				deferred : asset_selected
			})
			.done( function( rid ) {
				if ( rid > 0 ) {
					$.boom.page.toolbar.minimise();

					$.post( '/asset/embed/' + rid )
					.done( function( response ){
						img.replaceWith(response);
						$(response).on('load', function() {
							self.instance.composer.editableArea.style.height = self.instance.composer.element.scrollHeight + "px";
						});
					})
					.always( function(){
						asset_selected.reject();
					});
				}

			})
			.fail( function(){
				img.remove();
			});
	},

	/**
	@function
	@returns {Deferred}
	*/
	_edit_link : function() {
		var self = this;
		var ed = self.instance.composer;
		var existing_link = ed.commands.state( "createLink" )[0];
		var opts = {};
		var bm = ed.selection.getBookmark();

		if ( existing_link ) {
			var link = {
				url : existing_link.href,
				rid : existing_link.rel,
				title : ( existing_link.textContent || existinglink.innerText )
			};

			opts.link = link;
		}

		 return $.boom.links
			.picker( opts )
			.fail( function(){
				var link = $( ed.element ).find( '[rel=new-link]' );

				link
					.after( link.text() )
					.remove();

			})
			.done( function( link ){

				var uri = link.url;
				var page_id = link.page_id;

				if (page_id) {
					uri = $('').attr('href',uri)[0].pathname.replace(/^[^\/]/,'/');
				}

				if ( existing_link ) {
					existing_link.textContent = existing_link.textContent.replace(existing_link.href, uri);

					$(existing_link)
						.attr('href', uri)
						.attr('title', '')
						.attr('rel', page_id);
				} else {
					ed.selection.setBookmark(bm);

					if (page_id) {
						ed.commands.exec("createLink", { href: uri, rel: page_id, title: '', text: link.title});
					} else {
						var text = uri.replace('mailto:', '').replace('tel:', '');

						ed.commands.exec("createLink", { href: uri, title: '', text: text});
					}
				}

			});

	},

	/**
	@function
	@param {Object} wh5 Instance of wysihtml5
	*/
	_load_extensions : function( wh5 ) {

		/** blockquote command */
		(function(wysihtml5) {
			var undef;

			/**
			Indent command for blockquotes
			@static
			@class
			**/
			wysihtml5.commands.indent = {
				exec: function(composer, command) {

					command = this.state( composer ) ? 'outdent' : 'indent';

					/*
					TODO: what happens if a browser doesn't support indent/outdent?
					*/
					if (composer.commands.support(command)) {
						composer.doc.execCommand(command, false, null);

						/* clean inline styles left by webkit */
						var blockquote = this.state( composer );
						if ( blockquote ) {
							blockquote.removeAttribute( 'class' );
							blockquote.removeAttribute( 'style' );
						}
						return;
					}
				},

				state: function(composer) {
					var selectedNode = composer.selection.getSelectedNode();
					return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "BLOCKQUOTE" });
				},

				value: function() {
					return undef;
				}
			};
		})( wh5 );
	}
});<commit_msg>Removed console logging<commit_after>/**
@fileOverview Boom interface for wysihtml5.
*/
/**
* Interface for the wysihtml5 editor.
* @class
* @name $.wysihtml5.editor
* @extends $.boom.editor
*/
$.widget('wysihtml5.editor', $.boom.textEditor,
	/** @lends $.wysihtml5.editor */
	{
	/**
	@property mode
	@type string
	*/
	mode : 'block',

	/**
	@property options
	@type object
	*/
	options : {
	},

	/**
	* @function
	@returns {Deferred}
	*/
	load : function() {
		this._load_extensions(wysihtml5);
	},

	/**
	* @function
	@param {Object} element The element being edited.
	@returns {Deferred}
	*/
	edit : function (element) {
		var self = this,
			element;

		self.mode = element.is('div') ? 'block' : 'inline';
		self.mode = (element.is(':header') ||  element.is('.standFirst'))? 'text' : self.mode;
		self.edited = new $.Deferred();
		self.original_html = element.html();

		self._insert_toolbar(element)
			.done(function() {
				self.instance = new wysihtml5.Editor(element[0], { // id of textarea element
					toolbar : 'wysihtml5-toolbar',
					style : true,
					parserRules :  wysihtml5ParserRules, // defined in parser rules set
					useLineBreaks : false,
					contentEditableMode : true
				});

				$('#wysihtml5-toolbar')
					.on('click', '#b-editor-link', function(e) {
						e.preventDefault();
						var href = $( '[data-wysihtml5-dialog-field=href]' ).val();
						var match = href.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );
						var asset_id = match ? match[2] : 0;
						if (asset_id == 0) {
							self._edit_link();
						} else {
							self._edit_asset(asset_id);
							resizeIframe();
						}
					})
					.on('click', '#b-editor-asset', function(e) {
						e.preventDefault();
						var src = $( '[data-wysihtml5-dialog-field=src]' ).val();
						var asset_id = 0;
						if ( src && src != 'http://' ) {
							var match = src.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );

							asset_id = match ? match[2] : 0;
						}

						self._edit_asset(asset_id);
					})
					.on('click', '#b-editor-accept', function(event) {
						event.preventDefault();
						self.apply(element);
						return false;
					})
					.on( 'click', '#b-editor-cancel', function( event ){
						event.preventDefault();
						self.cancel(element);
						return false;
					});

				self.instance.on( 'show:dialog', function( options ){
					switch( options.command ) {
						case 'createLink' :
							var href = $( '[data-wysihtml5-dialog-field=href]' ).val();

							if ( ! href || href == 'http://') {
								self._edit_link();
							}
							break;
						case 'insertImage' :
							var src = $( '[data-wysihtml5-dialog-field=src]' ).val();
							var asset_id = 0;
							if ( src && src != 'http://' ) {
								var match = src.match( /asset\/(thumb|view|get_asset)\/([0-9]+)/ );

								asset_id = match ? match[2] : 0;
							}

							if ( ! asset_id) {
								self._edit_asset(asset_id);
							}
							break;
					}
				});
			});

		return self.edited;

	},

	/**
	* @function
	*/
	remove : function(element) {
		$('#wysihtml5-toolbar').remove();
		element.blur();
		element.removeAttr('contenteditable');

		this.instance = null;
	},

	/**
	* @function
	@param {Object} element The element being edited.
	*/
	apply : function(element) {
		element.blur();
		element.removeAttr('contenteditable');
		this.edited.resolve(element.html());

		this.remove(element);
	},

	/**
	* @function
	@param {Object} element The element being edited.
	*/
	cancel : function(element) {
		var self = this,
			content = element.html();

		if (content != self.original_html) {
			$.boom.dialog.confirm('Cancel changes', 'Cancel all changes and exit the editor?')
				.done(function() {
					$.boom.log( 'canceling text edits' );

					self.remove(element);

					self.edited.reject();
				});
		} else {
			self.remove(element);
			self.edited.reject();
		}
	},

	/**
	@function
	@param {Object} element The element being edited.
	@returns {Deferred}
	*/
	_insert_toolbar : function(element) {
		var self = this;

		 return $.get('/media/boom/toolbars/text.php?mode=' + self.mode)
			.done(function(response) {
				$('body').prepend(response)
			});
	},

	/**
	@function
	@returns {Deferred}
	*/
	_edit_asset : function(asset_rid) {
		var self = this,
			ed = self.instance.composer,
			asset_selected = new $.Deferred(),
			img;

		if (asset_rid == 0) {
			ed.commands.exec("insertHTML", '');
			img = $(ed.element).find( '[src=url]' );
			if ( ! img.length)
				img = $(ed.element).find( '[href=url]' );
		} else {
			img = $(ed.element).find( '[src^="/asset/view/' + asset_rid +'"]' );
			if ( ! img.length)
				img = $(ed.element).find( '[href^="/asset/view/' + asset_rid +'"]' );
		}

		// cleanup code when the dialog closes.
		asset_selected
		.fail( function() {
			$( ed.element ).find( '[src=url]' ).remove() || $( ed.element ).find( '[href=url]' ).remove();
		});

		return $.boom.assets
			.picker({
				asset_rid : asset_rid,
				deferred : asset_selected
			})
			.done( function( rid ) {
				if ( rid > 0 ) {
					$.boom.page.toolbar.minimise();

					$.post( '/asset/embed/' + rid )
					.done( function( response ){
						img.replaceWith(response);
						$(response).on('load', function() {
							self.instance.composer.editableArea.style.height = self.instance.composer.element.scrollHeight + "px";
						});
					})
					.always( function(){
						asset_selected.reject();
					});
				}

			})
			.fail( function(){
				img.remove();
			});
	},

	/**
	@function
	@returns {Deferred}
	*/
	_edit_link : function() {
		var self = this;
		var ed = self.instance.composer;
		var existing_link = ed.commands.state( "createLink" )[0];
		var opts = {};
		var bm = ed.selection.getBookmark();

		if ( existing_link ) {
			var link = {
				url : existing_link.href,
				rid : existing_link.rel,
				title : ( existing_link.textContent || existinglink.innerText )
			};

			opts.link = link;
		}

		 return $.boom.links
			.picker( opts )
			.fail( function(){
				var link = $( ed.element ).find( '[rel=new-link]' );

				link
					.after( link.text() )
					.remove();

			})
			.done( function( link ){

				var uri = link.url;
				var page_id = link.page_id;

				if (page_id) {
					uri = $('').attr('href',uri)[0].pathname.replace(/^[^\/]/,'/');
				}

				if ( existing_link ) {
					existing_link.textContent = existing_link.textContent.replace(existing_link.href, uri);

					$(existing_link)
						.attr('href', uri)
						.attr('title', '')
						.attr('rel', page_id);
				} else {
					ed.selection.setBookmark(bm);

					if (page_id) {
						ed.commands.exec("createLink", { href: uri, rel: page_id, title: '', text: link.title});
					} else {
						var text = uri.replace('mailto:', '').replace('tel:', '');

						ed.commands.exec("createLink", { href: uri, title: '', text: text});
					}
				}

			});

	},

	/**
	@function
	@param {Object} wh5 Instance of wysihtml5
	*/
	_load_extensions : function( wh5 ) {

		/** blockquote command */
		(function(wysihtml5) {
			var undef;

			/**
			Indent command for blockquotes
			@static
			@class
			**/
			wysihtml5.commands.indent = {
				exec: function(composer, command) {

					command = this.state( composer ) ? 'outdent' : 'indent';

					/*
					TODO: what happens if a browser doesn't support indent/outdent?
					*/
					if (composer.commands.support(command)) {
						composer.doc.execCommand(command, false, null);

						/* clean inline styles left by webkit */
						var blockquote = this.state( composer );
						if ( blockquote ) {
							blockquote.removeAttribute( 'class' );
							blockquote.removeAttribute( 'style' );
						}
						return;
					}
				},

				state: function(composer) {
					var selectedNode = composer.selection.getSelectedNode();
					return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "BLOCKQUOTE" });
				},

				value: function() {
					return undef;
				}
			};
		})( wh5 );
	}
});<|endoftext|>


<commit_before>/* eslint-disable no-underscore-dangle */

import { Checkbox, Button, Col, Row, Grid, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';

import getCSRF from '../../../../shared/util/getcsrf';
import Utility from '../../../../shared/util/Utility';
import * as Security from '../../../../shared/reducers/Security';

export const Reducers = [Security];

export class loginScreen extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      checkboxChecked: false,
      username: '',
      password: '',
      _csrf: getCSRF(),
      submitLogin: true,
      canSubmit: false
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
  }

  componentWillMount() {
    if (this.props.loggedIn) {
      this.props.setPageByName('/');
    } else {
      this.props.clearAuthResponse();
    }
  }

  setLoginSubmitMode(submitLogin) {
    this.setState({
      submitLogin
    });
  }

  getValidationState() {
    // success, warning, error, or null
    if (this.haveFailedAuth()) {
      return 'error';
    } else if (this.haveSucceededAuth()) {
      return 'success';
    }

    return null;
  }

  handleChange(e) {
    this.setState({ checkboxChecked: e.target.checked });
  }

  handleInputChange(e) {
    this.setState({ [e.target.name]: e.target.value });
  }

  handleSubmit(formData) {
    formData.preventDefault();

    if (this.state.submitLogin) {
      this.props.requestUserLogin(formData, this.props.redirectUrl || '/');
    } else {
      this.props.requestUserRegister(formData, this.props.redirectUrl || '/');
    }
  }

  haveSucceededAuth() {
    return (
      !this.props.loginInProgress &&
      !this.props.registerInProgress &&
      this.props.authSuccess &&
      this.props.authResponse !== ''
    );
  }
  haveFailedAuth() {
    return (
      !this.props.loginInProgress &&
      !this.props.registerInProgress &&
      !this.props.authSuccess &&
      this.props.authResponse !== ''
    );
  }
  render() {
    return (
      <Grid>
        <Row>
          <Col
            mdOffset={0}
            md={4}
          >

Registrera dig snabbt och enkelt här

                Vi sparar inga personuppgifter så var noga med att komma ihåg ditt lösenord då vi inte kan återställa
                det åt dig.

                Materialet är anpassat efter det svenska språket och du kan lära dig från svenska till japanska och
                japanska till svenska

          </Col>
          <Col
            mdOffset={1}
            md={4}
          >
            <Form onSubmit={this.handleSubmit}>
              <fieldset>
                <FormGroup
                  controlId="formBasicText"
                  validationState={this.getValidationState()}
                >
                  <legend>Logga in eller registrera dig</legend>
                  {this.props.authResponse && this.getValidationState() ? (
                    <ControlLabel name="authFeedback">{this.props.authResponse}</ControlLabel>
                  ) : null}
                </FormGroup>
                <FormGroup>
                  <FormControl
                    type="text"
                    name="username"
                    placeholder="Användarnamn"
                    value={this.state.username}
                    onChange={this.handleInputChange}
                  />
                  <FormControl.Feedback />
                </FormGroup>
                <FormGroup>
                  <FormControl
                    type="password"
                    name="password"
                    placeholder="Lösenord"
                    value={this.state.password}
                    onChange={this.handleInputChange}
                  />
                </FormGroup>
                <FormControl
                  type="hidden"
                  name="_csrf"
                  value={this.state._csrf}
                />
                <FormGroup>
                  <Button
                    label="login"
                    onClick={() => this.setLoginSubmitMode(true)}
                    bsStyle="primary"
                    name="login"
                    type="submit"
                    disabled={!this.state.username || !this.state.password}
                  >
                    Logga in
                  </Button>{' '}
                  <Button
                    label="login"
                    onClick={() => this.setLoginSubmitMode(false)}
                    bsStyle="success"
                    name="register"
                    type="submit"
                    disabled={!this.state.username || !this.state.password}
                  >
                    Registrera
                  </Button>{' '}
                  <Checkbox
                    label="remember-me"
                    name="remember-me"
                    checked={this.state.checkboxChecked}
                    onChange={this.handleChange}
                  >
                    Håll mig inloggad
                  </Checkbox>{' '}
                </FormGroup>
              </fieldset>
            </Form>
          </Col>
        </Row>
      </Grid>
    );
  }
}

loginScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

loginScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(loginScreen);
<commit_msg>validates<commit_after>/* eslint-disable no-underscore-dangle */

import { Checkbox, Button, Col, Row, Grid, Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap';

import getCSRF from '../../../../shared/util/getcsrf';
import Utility from '../../../../shared/util/Utility';
import * as Security from '../../../../shared/reducers/Security';

export const Reducers = [Security];

export class loginScreen extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      checkboxChecked: false,
      username: '',
      password: '',
      _csrf: getCSRF(),
      submitLogin: true,
      canSubmit: false,
      invalidUsername: false
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this);
  }

  componentWillMount() {
    if (this.props.loggedIn) {
      this.props.setPageByName('/');
    } else {
      this.props.clearAuthResponse();
    }
  }

  setLoginSubmitMode(submitLogin) {
    this.setState({
      submitLogin
    });
  }

  getValidationState() {
    // success, warning, error, or null
    if (this.haveFailedAuth()) {
      return 'error';
    } else if (this.haveSucceededAuth()) {
      return 'success';
    }

    return null;
  }
  handleChange(e) {
    this.setState({ checkboxChecked: e.target.checked });
  }

  handleInputChange(e) {
    this.setState({ [e.target.name]: e.target.value });
    if(e.target.name==='username'){this.validateUsername(e)}
  }
  validateUsername(e){
      let regex = RegExp('[^A-Za-z0-9]+');
      this.setState({invalidUsername : regex.test(e.target.value)});
  }

  handleSubmit(formData) {
    formData.preventDefault();

    if (this.state.submitLogin) {
      this.props.requestUserLogin(formData, this.props.redirectUrl || '/');
    } else {
      this.props.requestUserRegister(formData, this.props.redirectUrl || '/');
    }
  }

  haveSucceededAuth() {
    return (
      !this.props.loginInProgress &&
      !this.props.registerInProgress &&
      this.props.authSuccess &&
      this.props.authResponse !== ''
    );
  }
  haveFailedAuth() {
    return (
      !this.props.loginInProgress &&
      !this.props.registerInProgress &&
      !this.props.authSuccess &&
      this.props.authResponse !== ''
    );
  }
  render() {
    return (
      <Grid>
        <Row>
          <Col
            mdOffset={0}
            md={4}
          >

Registrera dig snabbt och enkelt här

                Vi sparar inga personuppgifter så var noga med att komma ihåg ditt lösenord då vi inte kan återställa
                det åt dig.

                Materialet är anpassat efter det svenska språket och du kan lära dig från svenska till japanska och
                japanska till svenska

          </Col>
          <Col
            mdOffset={1}
            md={4}
          >
            <Form onSubmit={this.handleSubmit}>
              <fieldset>
                <FormGroup
                  controlId="formBasicText"
                  validationState={this.getValidationState()}
                >
                  <legend>Logga in eller registrera dig</legend>
                  {this.props.authResponse && this.getValidationState() ? (
                    <ControlLabel name="authFeedback">{this.props.authResponse}</ControlLabel>
                  ) : null}
                </FormGroup>
                <FormGroup>
                  <FormControl
                    type="text"
                    name="username"
                    placeholder="Användarnamn"
                    value={this.state.username}
                    onChange={this.handleInputChange}
                  />
                    {this.state.invalidUsername===true ?

                            Ett användarnamn kan endast innehålla bokstäver och nummer

 : null}
                  <FormControl.Feedback />
                </FormGroup>
                <FormGroup>
                  <FormControl
                    type="password"
                    name="password"
                    placeholder="Lösenord"
                    value={this.state.password}
                    onChange={this.handleInputChange}
                  />
                </FormGroup>
                <FormControl
                  type="hidden"
                  name="_csrf"
                  value={this.state._csrf}
                />
                <FormGroup>
                  <Button
                    label="login"
                    onClick={() => this.setLoginSubmitMode(true)}
                    bsStyle="primary"
                    name="login"
                    type="submit"
                    disabled={!this.state.username || !this.state.password}
                  >
                    Logga in
                  </Button>{' '}
                  <Button
                    label="login"
                    onClick={() => this.setLoginSubmitMode(false)}
                    bsStyle="success"
                    name="register"
                    type="submit"
                    disabled={!this.state.username || !this.state.password || this.state.invalidUsername}
                  >
                    Registrera
                  </Button>{' '}
                  <Checkbox
                    label="remember-me"
                    name="remember-me"
                    checked={this.state.checkboxChecked}
                    onChange={this.handleChange}
                  >
                    Håll mig inloggad
                  </Checkbox>{' '}
                </FormGroup>
              </fieldset>
            </Form>
          </Col>
        </Row>
      </Grid>
    );
  }
}

loginScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

loginScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(loginScreen);
<|endoftext|>


<commit_before>import filter from '../util/filter'
import forEach from '../util/forEach'
import isArrayEqual from '../util/isArrayEqual'
import DocumentIndex from './DocumentIndex'
import ObjectOperation from './data/ObjectOperation'
import DocumentChange from './DocumentChange'
import annotationHelpers from './annotationHelpers'
import { isEntirelySelected } from './selectionHelpers'

/**
  Some helpers for working with Documents.

  @module
  @example

  ```js
  import { documentHelpers } from 'substance'
  documentHelpers.isContainerAnnotation(doc, 'comment')
  ```
*/
export default {
  isContainerAnnotation,
  getPropertyAnnotationsForSelection,
  getContainerAnnotationsForSelection,
  getAnnotationsForSelection,
  getTextForSelection,
  getMarkersForSelection,
  getChangeFromDocument,
  copyNode,
  deleteNode,
  deleteTextRange,
  deleteListRange,
  mergeListItems
}

/**
  @param {Document} doc
  @param {String} type
  @return {Boolean} `true` if given type is a {@link ContainerAnnotation}
*/
function isContainerAnnotation(doc, type) {
  var schema = doc.getSchema();
  return schema.isInstanceOf(type, 'container-annotation');
}

/**
  For a given selection get all property annotations

  @param {Document} doc
  @param {Selection} sel
  @return {PropertyAnnotation[]} An array of property annotations.
          Returns an empty array when selection is a container selection.
*/
function getPropertyAnnotationsForSelection(doc, sel, options) {
  options = options || {};
  if (!sel.isPropertySelection()) {
    return [];
  }
  let path = sel.getPath()
  let annotations = doc.getIndex('annotations').get(path, sel.start.offset, sel.end.offset);
  if (options.type) {
    annotations = filter(annotations, DocumentIndex.filterByType(options.type));
  }
  return annotations;
}

/**
  For a given selection get all container annotations

  @param {Document} doc
  @param {Selection} sel
  @param {String} containerId
  @param {String} options.type provides only annotations of that type
  @return {Array} An array of container annotations
*/
function getContainerAnnotationsForSelection(doc, sel, containerId, options) {
  // ATTENTION: looking for container annotations is not as efficient as property
  // selections, as we do not have an index that has notion of the spatial extend
  // of an annotation. Opposed to that, common annotations are bound
  // to properties which make it easy to lookup.
  if (!containerId) {
    throw new Error("'containerId' is required.");
  }
  options = options || {};
  var index = doc.getIndex('container-annotations');
  var annotations = index.get(containerId, options.type);
  annotations = filter(annotations, function(anno) {
    return sel.overlaps(anno.getSelection());
  });
  return annotations;
}

/**
  For a given selection, get annotations of a certain type

  @param {Document} doc
  @param {Selection} sel
  @param {string} annotationType
  @param {string} containerId (only needed when type is a container annotation)
  @return {array} all matching annotations
*/
function getAnnotationsForSelection(doc, sel, annotationType, containerId) {
  var annos;
  var isContainerAnno = isContainerAnnotation(doc, annotationType);

  if (isContainerAnno) {
    var container = doc.get(containerId, 'strict');
    annos = getContainerAnnotationsForSelection(doc, sel, container, {
      type: annotationType
    });
  } else {
    annos = getPropertyAnnotationsForSelection(doc, sel, { type: annotationType });
  }
  return annos;
}

/**
  For a given selection, get the corresponding text string

  @param {Document} doc
  @param {Selection} sel
  @return {string} text enclosed by the annotation
*/
function getTextForSelection(doc, sel) {
  if (!sel || sel.isNull()) {
    return "";
  } else if (sel.isPropertySelection()) {
    let text = doc.get(sel.start.path);
    return text.substring(sel.start.offset, sel.end.offset);
  } else if (sel.isContainerSelection()) {
    let result = []
    let nodeIds = sel.getNodeIds()
    let L = nodeIds.length
    for (let i = 0; i < L; i++) {
      let id = nodeIds[i]
      let node = doc.get(id)
      if (node.isText()) {
        let text = node.getText()
        if (i === L-1) {
          text = text.slice(0, sel.end.offset)
        }
        if (i === 0) {
          text = text.slice(sel.start.offset)
        }
        result.push(text)
      }
    }
    return result.join('\n');
  }
}

function getMarkersForSelection(doc, sel) {
  // only PropertySelections are supported right now
  if (!sel || !sel.isPropertySelection()) return []
  const path = sel.getPath()
  // markers are stored as one hash for each path, grouped by marker key
  let markers = doc.getIndex('markers').get(path)
  const filtered = filter(markers, function(m) {
    return m.isInsideOf(sel)
  })
  return filtered
}

function getChangeFromDocument(doc) {
  let nodes = doc.getNodes()
  let annotations = []
  let contentNodes = []
  let containers = []

  forEach(nodes, (node) => {
    if (node._isAnnotation) {
      annotations.push(node)
    } else if (node._isContainer) {
      containers.push(node)
    } else {
      contentNodes.push(node)
    }
  })

  let ops = contentNodes.concat(containers).concat(annotations).map((node) => {
    return ObjectOperation.Create([node.id], node.toJSON())
  })

  return new DocumentChange({ops: ops})
}

/*
  Deletes a node and its children and attached annotations
  and removes it from a given container
*/
function deleteNode(doc, node) {
  if (node.isText()) {
    // remove all associated annotations
    let annos = doc.getIndex('annotations').get(node.id)
    for (let i = 0; i < annos.length; i++) {
      doc.delete(annos[i].id);
    }
  }
  // delete recursively
  // ATM we do a cascaded delete if the property has type 'id' or ['array', 'id'] and property 'owned' set,
  // or if it 'file'
  let nodeSchema = node.getSchema()
  forEach(nodeSchema, (prop) => {
    if ((prop.isReference() && prop.isOwned()) || (prop.type === 'file')) {
      if (prop.isArray()) {
        let ids = node[prop.name]
        ids.forEach((id) => {
          deleteNode(doc, doc.get(id))
        })
      } else {
        deleteNode(doc, doc.get(node[prop.name]))
      }
    }
  })
  doc.delete(node.id)
}

/*
  Creates a 'deep' JSON copy of a node returning an array of JSON objects
  that can be used to create the object tree owned by the given root node.

  @param {DocumentNode} node
*/
function copyNode(node) {
  let nodes = []
  // EXPERIMENTAL: using schema reflection to determine whether to do a 'deep' copy or just shallow
  let nodeSchema = node.getSchema()
  let doc = node.getDocument()
  forEach(nodeSchema, (prop) => {
    // ATM we do a cascaded copy if the property has type 'id', ['array', 'id'] and is owned by the node,
    // or it is of type 'file'
    if ((prop.isReference() && prop.isOwned()) || (prop.type === 'file')) {
      let val = node[prop.name]
      if (prop.isArray()) {
        val.forEach((id) => {
          nodes = nodes.concat(copyNode(doc.get(id)))
        })
      } else {
        nodes = nodes.concat(copyNode(doc.get(val)))
      }
    }
  })
  nodes.push(node.toJSON())
  let annotationIndex = node.getDocument().getIndex('annotations')
  let annotations = annotationIndex.get([node.id])
  forEach(annotations, function(anno) {
    nodes.push(anno.toJSON())
  })
  return nodes
}

/*
  <-->: anno
  |--|: area of change
  I: <--> |--|     :   nothing
  II: |--| <-->    :   move both by total span
  III: |-<-->-|    :   delete anno
  IV: |-<-|->      :   move start by diff to start, and end by total span
  V: <-|->-|       :   move end by diff to start
  VI: <-|--|->     :   move end by total span
*/
function deleteTextRange(doc, start, end) {
  if (!start) {
    start = {
      path: end.path,
      offset: 0
    }
  }
  let path = start.path
  let node = doc.get(path[0])
  if (!node.isText()) throw new Error('Expecting a TextNode.')
  if (!end) {
    end = {
      path: start.path,
      offset: node.getLength()
    }
  }
  if (!isArrayEqual(start.path, end.path)) throw new Error('Unsupported state: selection should be on one property')
  let startOffset = start.offset
  let endOffset = end.offset
  doc.update(path, { type: 'delete', start: startOffset, end: endOffset })
  // update annotations
  let annos = doc.getAnnotations(path)
  annos.forEach(function(anno) {
    let annoStart = anno.start.offset
    let annoEnd = anno.end.offset
    // I anno is before
    if (annoEnd<=startOffset) {
      return
    }
    // II anno is after
    else if (annoStart>=endOffset) {
      doc.update([anno.id, 'start'], { type: 'shift', value: startOffset-endOffset })
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    // III anno is deleted
    else if (annoStart>=startOffset && annoEnd<=endOffset) {
      doc.delete(anno.id)
    }
    // IV anno.start between and anno.end after
    else if (annoStart>=startOffset && annoEnd>=endOffset) {
      if (annoStart>startOffset) {
        doc.update([anno.id, 'start'], { type: 'shift', value: startOffset-annoStart })
      }
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    // V anno.start before and anno.end between
    else if (annoStart<=startOffset && annoEnd<=endOffset) {
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-annoEnd })
    }
    // VI anno.start before and anno.end after
    else if (annoStart<startOffset && annoEnd >= endOffset) {
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    else {
      console.warn('TODO: handle annotation update case.')
    }
  })
}

function deleteListRange(doc, list, start, end) {
  if (doc !== list.getDocument()) {
    list = doc.get(list.id)
  }
  if (!start) {
    start = {
      path: list.getItemAt(0).getTextPath(),
      offset: 0
    }
  }
  if (!end) {
    let item = list.getLastItem()
    end = {
      path: item.getTextPath(),
      offset: item.getLength()
    }
  }
  let startId = start.path[0]
  let startPos = list.getItemPosition(startId)
  let endId = end.path[0]
  let endPos = list.getItemPosition(endId)
  // range within the same item
  if (startPos === endPos) {
    deleteTextRange(doc, start, end)
    return
  }
  // normalize the range if it is 'reverse'
  if (startPos > endPos) {
    [start, end] = [end, start];
    [startPos, endPos] = [endPos, startPos];
    [startId, endId] = [endId, startId]
  }
  let firstItem = doc.get(startId)
  let lastItem = doc.get(endId)
  let firstEntirelySelected = isEntirelySelected(doc, firstItem, start, null)
  let lastEntirelySelected = isEntirelySelected(doc, lastItem, null, end)

  // delete or truncate last node
  if (lastEntirelySelected) {
    list.removeItemAt(endPos)
    deleteNode(doc, lastItem)
  } else {
    deleteTextRange(doc, null, end)
  }

  // delete inner nodes
  for (let i = endPos-1; i > startPos; i--) {
    let itemId = list.items[i]
    list.removeItemAt(i)
    deleteNode(doc, doc.get(itemId))
  }

  // delete or truncate the first node
  if (firstEntirelySelected) {
    list.removeItemAt(startPos)
    deleteNode(doc, firstItem)
  } else {
    deleteTextRange(doc, start, null)
  }

  if (!firstEntirelySelected && !lastEntirelySelected) {
    mergeListItems(doc, list.id, startPos)
  }
}

function mergeListItems(doc, listId, itemPos) {
  // HACK: make sure that the list is really from the doc
  let list = doc.get(listId)
  let target = list.getItemAt(itemPos)
  let targetPath = target.getTextPath()
  let targetLength = target.getLength()
  let source = list.getItemAt(itemPos+1)
  let sourcePath = source.getTextPath()
  // hide source
  list.removeItemAt(itemPos+1)
  // append the text
  doc.update(targetPath, { type: 'insert', start: targetLength, text: source.getText() })
  // transfer annotations
  annotationHelpers.transferAnnotations(doc, sourcePath, 0, targetPath, targetLength)
  doc.delete(source.id)
}

<commit_msg>Forgiving.<commit_after>import filter from '../util/filter'
import forEach from '../util/forEach'
import isArrayEqual from '../util/isArrayEqual'
import DocumentIndex from './DocumentIndex'
import ObjectOperation from './data/ObjectOperation'
import DocumentChange from './DocumentChange'
import annotationHelpers from './annotationHelpers'
import { isEntirelySelected } from './selectionHelpers'

/**
  Some helpers for working with Documents.

  @module
  @example

  ```js
  import { documentHelpers } from 'substance'
  documentHelpers.isContainerAnnotation(doc, 'comment')
  ```
*/
export default {
  isContainerAnnotation,
  getPropertyAnnotationsForSelection,
  getContainerAnnotationsForSelection,
  getAnnotationsForSelection,
  getTextForSelection,
  getMarkersForSelection,
  getChangeFromDocument,
  copyNode,
  deleteNode,
  deleteTextRange,
  deleteListRange,
  mergeListItems
}

/**
  @param {Document} doc
  @param {String} type
  @return {Boolean} `true` if given type is a {@link ContainerAnnotation}
*/
function isContainerAnnotation(doc, type) {
  var schema = doc.getSchema();
  return schema.isInstanceOf(type, 'container-annotation');
}

/**
  For a given selection get all property annotations

  @param {Document} doc
  @param {Selection} sel
  @return {PropertyAnnotation[]} An array of property annotations.
          Returns an empty array when selection is a container selection.
*/
function getPropertyAnnotationsForSelection(doc, sel, options) {
  options = options || {};
  if (!sel.isPropertySelection()) {
    return [];
  }
  let path = sel.getPath()
  let annotations = doc.getIndex('annotations').get(path, sel.start.offset, sel.end.offset);
  if (options.type) {
    annotations = filter(annotations, DocumentIndex.filterByType(options.type));
  }
  return annotations;
}

/**
  For a given selection get all container annotations

  @param {Document} doc
  @param {Selection} sel
  @param {String} containerId
  @param {String} options.type provides only annotations of that type
  @return {Array} An array of container annotations
*/
function getContainerAnnotationsForSelection(doc, sel, containerId, options) {
  // ATTENTION: looking for container annotations is not as efficient as property
  // selections, as we do not have an index that has notion of the spatial extend
  // of an annotation. Opposed to that, common annotations are bound
  // to properties which make it easy to lookup.
  if (!containerId) {
    throw new Error("'containerId' is required.");
  }
  options = options || {};
  var index = doc.getIndex('container-annotations');
  var annotations = index.get(containerId, options.type);
  annotations = filter(annotations, function(anno) {
    return sel.overlaps(anno.getSelection());
  });
  return annotations;
}

/**
  For a given selection, get annotations of a certain type

  @param {Document} doc
  @param {Selection} sel
  @param {string} annotationType
  @param {string} containerId (only needed when type is a container annotation)
  @return {array} all matching annotations
*/
function getAnnotationsForSelection(doc, sel, annotationType, containerId) {
  var annos;
  var isContainerAnno = isContainerAnnotation(doc, annotationType);

  if (isContainerAnno) {
    var container = doc.get(containerId, 'strict');
    annos = getContainerAnnotationsForSelection(doc, sel, container, {
      type: annotationType
    });
  } else {
    annos = getPropertyAnnotationsForSelection(doc, sel, { type: annotationType });
  }
  return annos;
}

/**
  For a given selection, get the corresponding text string

  @param {Document} doc
  @param {Selection} sel
  @return {string} text enclosed by the annotation
*/
function getTextForSelection(doc, sel) {
  if (!sel || sel.isNull()) {
    return "";
  } else if (sel.isPropertySelection()) {
    let text = doc.get(sel.start.path);
    return text.substring(sel.start.offset, sel.end.offset);
  } else if (sel.isContainerSelection()) {
    let result = []
    let nodeIds = sel.getNodeIds()
    let L = nodeIds.length
    for (let i = 0; i < L; i++) {
      let id = nodeIds[i]
      let node = doc.get(id)
      if (node.isText()) {
        let text = node.getText()
        if (i === L-1) {
          text = text.slice(0, sel.end.offset)
        }
        if (i === 0) {
          text = text.slice(sel.start.offset)
        }
        result.push(text)
      }
    }
    return result.join('\n');
  }
}

function getMarkersForSelection(doc, sel) {
  // only PropertySelections are supported right now
  if (!sel || !sel.isPropertySelection()) return []
  const path = sel.getPath()
  // markers are stored as one hash for each path, grouped by marker key
  let markers = doc.getIndex('markers').get(path)
  const filtered = filter(markers, function(m) {
    return m.isInsideOf(sel)
  })
  return filtered
}

function getChangeFromDocument(doc) {
  let nodes = doc.getNodes()
  let annotations = []
  let contentNodes = []
  let containers = []

  forEach(nodes, (node) => {
    if (node._isAnnotation) {
      annotations.push(node)
    } else if (node._isContainer) {
      containers.push(node)
    } else {
      contentNodes.push(node)
    }
  })

  let ops = contentNodes.concat(containers).concat(annotations).map((node) => {
    return ObjectOperation.Create([node.id], node.toJSON())
  })

  return new DocumentChange({ops: ops})
}

/*
  Deletes a node and its children and attached annotations
  and removes it from a given container
*/
function deleteNode(doc, node) {
  if (!node) {
    console.warn('Invalid arguments')
    return
  }
  if (node.isText()) {
    // remove all associated annotations
    let annos = doc.getIndex('annotations').get(node.id)
    for (let i = 0; i < annos.length; i++) {
      doc.delete(annos[i].id);
    }
  }
  // delete recursively
  // ATM we do a cascaded delete if the property has type 'id' or ['array', 'id'] and property 'owned' set,
  // or if it 'file'
  let nodeSchema = node.getSchema()
  forEach(nodeSchema, (prop) => {
    if ((prop.isReference() && prop.isOwned()) || (prop.type === 'file')) {
      if (prop.isArray()) {
        let ids = node[prop.name]
        ids.forEach((id) => {
          deleteNode(doc, doc.get(id))
        })
      } else {
        deleteNode(doc, doc.get(node[prop.name]))
      }
    }
  })
  doc.delete(node.id)
}

/*
  Creates a 'deep' JSON copy of a node returning an array of JSON objects
  that can be used to create the object tree owned by the given root node.

  @param {DocumentNode} node
*/
function copyNode(node) {
  let nodes = []
  // EXPERIMENTAL: using schema reflection to determine whether to do a 'deep' copy or just shallow
  let nodeSchema = node.getSchema()
  let doc = node.getDocument()
  forEach(nodeSchema, (prop) => {
    // ATM we do a cascaded copy if the property has type 'id', ['array', 'id'] and is owned by the node,
    // or it is of type 'file'
    if ((prop.isReference() && prop.isOwned()) || (prop.type === 'file')) {
      let val = node[prop.name]
      if (prop.isArray()) {
        val.forEach((id) => {
          nodes = nodes.concat(copyNode(doc.get(id)))
        })
      } else {
        nodes = nodes.concat(copyNode(doc.get(val)))
      }
    }
  })
  nodes.push(node.toJSON())
  let annotationIndex = node.getDocument().getIndex('annotations')
  let annotations = annotationIndex.get([node.id])
  forEach(annotations, function(anno) {
    nodes.push(anno.toJSON())
  })
  return nodes
}

/*
  <-->: anno
  |--|: area of change
  I: <--> |--|     :   nothing
  II: |--| <-->    :   move both by total span
  III: |-<-->-|    :   delete anno
  IV: |-<-|->      :   move start by diff to start, and end by total span
  V: <-|->-|       :   move end by diff to start
  VI: <-|--|->     :   move end by total span
*/
function deleteTextRange(doc, start, end) {
  if (!start) {
    start = {
      path: end.path,
      offset: 0
    }
  }
  let path = start.path
  let node = doc.get(path[0])
  if (!node.isText()) throw new Error('Expecting a TextNode.')
  if (!end) {
    end = {
      path: start.path,
      offset: node.getLength()
    }
  }
  if (!isArrayEqual(start.path, end.path)) throw new Error('Unsupported state: selection should be on one property')
  let startOffset = start.offset
  let endOffset = end.offset
  doc.update(path, { type: 'delete', start: startOffset, end: endOffset })
  // update annotations
  let annos = doc.getAnnotations(path)
  annos.forEach(function(anno) {
    let annoStart = anno.start.offset
    let annoEnd = anno.end.offset
    // I anno is before
    if (annoEnd<=startOffset) {
      return
    }
    // II anno is after
    else if (annoStart>=endOffset) {
      doc.update([anno.id, 'start'], { type: 'shift', value: startOffset-endOffset })
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    // III anno is deleted
    else if (annoStart>=startOffset && annoEnd<=endOffset) {
      doc.delete(anno.id)
    }
    // IV anno.start between and anno.end after
    else if (annoStart>=startOffset && annoEnd>=endOffset) {
      if (annoStart>startOffset) {
        doc.update([anno.id, 'start'], { type: 'shift', value: startOffset-annoStart })
      }
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    // V anno.start before and anno.end between
    else if (annoStart<=startOffset && annoEnd<=endOffset) {
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-annoEnd })
    }
    // VI anno.start before and anno.end after
    else if (annoStart<startOffset && annoEnd >= endOffset) {
      doc.update([anno.id, 'end'], { type: 'shift', value: startOffset-endOffset })
    }
    else {
      console.warn('TODO: handle annotation update case.')
    }
  })
}

function deleteListRange(doc, list, start, end) {
  if (doc !== list.getDocument()) {
    list = doc.get(list.id)
  }
  if (!start) {
    start = {
      path: list.getItemAt(0).getTextPath(),
      offset: 0
    }
  }
  if (!end) {
    let item = list.getLastItem()
    end = {
      path: item.getTextPath(),
      offset: item.getLength()
    }
  }
  let startId = start.path[0]
  let startPos = list.getItemPosition(startId)
  let endId = end.path[0]
  let endPos = list.getItemPosition(endId)
  // range within the same item
  if (startPos === endPos) {
    deleteTextRange(doc, start, end)
    return
  }
  // normalize the range if it is 'reverse'
  if (startPos > endPos) {
    [start, end] = [end, start];
    [startPos, endPos] = [endPos, startPos];
    [startId, endId] = [endId, startId]
  }
  let firstItem = doc.get(startId)
  let lastItem = doc.get(endId)
  let firstEntirelySelected = isEntirelySelected(doc, firstItem, start, null)
  let lastEntirelySelected = isEntirelySelected(doc, lastItem, null, end)

  // delete or truncate last node
  if (lastEntirelySelected) {
    list.removeItemAt(endPos)
    deleteNode(doc, lastItem)
  } else {
    deleteTextRange(doc, null, end)
  }

  // delete inner nodes
  for (let i = endPos-1; i > startPos; i--) {
    let itemId = list.items[i]
    list.removeItemAt(i)
    deleteNode(doc, doc.get(itemId))
  }

  // delete or truncate the first node
  if (firstEntirelySelected) {
    list.removeItemAt(startPos)
    deleteNode(doc, firstItem)
  } else {
    deleteTextRange(doc, start, null)
  }

  if (!firstEntirelySelected && !lastEntirelySelected) {
    mergeListItems(doc, list.id, startPos)
  }
}

function mergeListItems(doc, listId, itemPos) {
  // HACK: make sure that the list is really from the doc
  let list = doc.get(listId)
  let target = list.getItemAt(itemPos)
  let targetPath = target.getTextPath()
  let targetLength = target.getLength()
  let source = list.getItemAt(itemPos+1)
  let sourcePath = source.getTextPath()
  // hide source
  list.removeItemAt(itemPos+1)
  // append the text
  doc.update(targetPath, { type: 'insert', start: targetLength, text: source.getText() })
  // transfer annotations
  annotationHelpers.transferAnnotations(doc, sourcePath, 0, targetPath, targetLength)
  doc.delete(source.id)
}

<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs');

var appRoot = 'src/';
var testsRoot = 'tests/';
var tsGlob = '**/*.ts';

var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));

module.exports = {
  root: appRoot,
  transpiled: 'transpiled/',
  source: appRoot + tsGlob,
  tests: testsRoot + tsGlob,
  output: 'dist/',
  packageName: pkg.name,
  dtsSrc: [
    'typings/**/*.ts',
    './jspm_packages/**/*.d.ts'
  ]
};
<commit_msg>one more build<commit_after>var path = require('path');
var fs = require('fs');

var appRoot = 'src/';
var testsRoot = 'tests/';
var tsGlob = '**/*.ts';

var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));

module.exports = {
  root: appRoot,
  transpiled: 'transpiled/',
  source: appRoot + tsGlob,
  tests: testsRoot + tsGlob,
  output: 'dist/',
  packageName: pkg.name,
  dtsSrc: [
    'typings/**/*.ts'
  ]
};
<|endoftext|>


<commit_before>import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import createHistory from 'history/createBrowserHistory'
import { ConnectedRouter } from 'react-router-redux'

import Routes from './components/Routes'
import createStore from './store/create_store'
import deserializeInitialState from './store/deserialize_initial_state'

const routes = require($resolve.routes)

const history = createHistory({
  basename: process.env.ROOT_PATH
})

const initialState = deserializeInitialState(window.__INITIAL_STATE__)

const store = createStore(initialState, history)

render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Routes routes={routes} />
    </ConnectedRouter>
  </Provider>,
  document.getElementById('resolve-application-container')
)
<commit_msg>Fix client index.js<commit_after>import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import createHistory from 'history/createBrowserHistory'
import { ConnectedRouter } from 'react-router-redux'

import Routes from './components/Routes'
import createStore from './store/create_store'
import deserializeInitialState from './store/deserialize_initial_state'

const routes = require($resolve.routes)

const history = createHistory({
  basename: process.env.ROOT_PATH
})

const initialState = deserializeInitialState(window.__INITIAL_STATE__)

const store = createStore(initialState, history)

render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Routes routes={routes} />
    </ConnectedRouter>
  </Provider>,
  document.getElementsByClassName('app-container')[0]
)
<|endoftext|>