fossasia/loklak_webclient

View on GitHub
iframely/static/js/iframely.js

Summary

Maintainability
F
1 wk
Test Coverage
;(function ( $ ) {

    /*

     Iframely consumer client lib.

     Version 0.9.4

     Fetches and renders iframely oebmed/2 widgets.

     */

    var windowMessaging = function(){

        return {
            postMessage : function(message, target_url, target) {

                message = JSON.stringify(message);

                target_url = target_url || '*';

                target = target || window.parent;  // default to parent

                if (window['postMessage']) {
                    // the browser supports window.postMessage, so call it with a targetOrigin
                    // set appropriately, based on the target_url parameter.
                    target['postMessage'](message, target_url.replace( /([^:]+:\/\/[^\/]+).*/, '$1'));

                }
            },

            receiveMessage : function(callback) {

                function cb(e) {
                    var message;
                    try {
                        message = JSON.parse(e.data);
                    } catch (ex) {
                    }

                    callback(e, message);
                }

                // browser supports window.postMessage
                if (window['postMessage']) {
                    if (window['addEventListener']) {
                        window[callback ? 'addEventListener' : 'removeEventListener']('message', cb, !1);
                    } else {
                        window[callback ? 'attachEvent' : 'detachEvent']('onmessage', cb);
                    }
                }
            }
        };
    }();

    $.ajaxPrefilter('script', function(options) {
        if (options.url.indexOf("reader.js") > -1) {
            options.cache = true;
        }
    });

    $.iframely = $.iframely || {};
    $.iframely.iframes = $.iframely.iframes || {};

    windowMessaging.receiveMessage(function(e, message) {
        var $iframe;
        if (message && message.windowId && ($iframe = $.iframely.iframes[message.windowId])) {
            if ($.iframely.setIframeHeight && message.method === "resize" && message.height) {
                $.iframely.setIframeHeight($iframe, message.height);
            }
        }
    });

    $.iframely.setIframeHeight = function($iframe, height) {

        var responsive = $iframe.attr('iframely-wrapped');
        var $parent = $iframe.parent();

        if (typeof responsive === 'undefined') {

            // Detect if <iframe> in responsive container.
            var parentStyle = $parent.attr('style');
            var iframeStyle = $iframe.attr('style');

            if (parentStyle && iframeStyle &&
                parentStyle.match('position: relative;') &&
                parentStyle.match('height: 0px;') &&
                iframeStyle.match('height: 100%;') &&
                iframeStyle.match('width: 100%;')) {

                $iframe.attr('iframely-wrapped', true);
                responsive = true;

            } else {

                $iframe.attr('iframely-wrapped', false);
                responsive = false;
            }
        }

        if (responsive) {
            $parent
                .css('padding-bottom', '')
                .css('height', height);
        } else {
            $iframe.css('height', height);
        }
    };

    $.iframely.registerIframesIn = function($parent) {

        $parent.find('iframe').each(function() {
            var $iframe = $(this);
            $.iframely.registerIframe($iframe);
        });

    };

    $.iframely.registerIframe = function($iframe, id) {
        if (!$iframe || $iframe.attr('iframely-registered')) {
            return;
        }

        $iframe.attr('iframely-registered', true);

        $iframe.load(function() {

            var iframesCounter = $.iframely.iframesCounter = ($.iframely.iframesCounter || 0) + 1,
                windowId = id || iframesCounter;

            $.iframely.iframes[windowId] = $iframe;
            windowMessaging.postMessage({
                method: "register",
                windowId: windowId
            }, '*', this.contentWindow);
        });
    };

    $.iframely.getPageData = function(uri, options, cb) {

        if (typeof options === "function") {
            cb = options;
            options = {};
        }

        options = options || {};

        $.support.cors = true;
        $.ajax({
            crossDomain: true,
            url: $.iframely.defaults.endpoint,
            dataType: "json",
            data: {
                uri: !options.url ? uri : undefined,
                url: options.url ? uri : undefined,
                debug: options.debug,
                mixAllWithDomainPlugin: options.mixAllWithDomainPlugin,
                refresh: options.refresh,
                meta: options.meta,
                whitelist: options.whitelist,
                api_key: options.api_key,
                origin: options.origin,
                autoplay: options.autoplay,
                ssl: options.ssl,
                html5: options.html5,
                iframe: options.iframe
            },
            success: function(data, textStatus, jqXHR) {
                cb(null, data, jqXHR);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var responseJSON = function(){
                    try {
                        return JSON.parse(jqXHR.responseText)
                    } catch(e){};
                }();
                cb(textStatus, responseJSON, jqXHR);
            }
        });
    };

    $.iframely.defaults = {
        endpoint: "//iframely.com/iframely"
    };

    $.iframely.get = function(endpoint, query, cb) {
        $.ajax({
            url: endpoint,
            dataType: "json",
            data: query,
            success: function(data, textStatus, jqXHR) {
                cb(null, data, jqXHR);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                var responseJSON = function() {
                    try {
                        return JSON.parse(jqXHR.responseText);
                    } catch(e) {};
                }();

                cb((responseJSON && responseJSON.error) || jqXHR.status || errorThrown.message, responseJSON, jqXHR);
            }
        });
    }

    function wrapContainer($element, data) {

        var media = data.media;

        if (media && media.height && media.width && !media["aspect-ratio"]) {
            $element.attr('width', media.width);
            $element.attr('height', media.height);
            return $element;
        }

        $element.css('top', 0)
            .css('left', 0)
            .css('width', '100%')
            .css('height', '100%')
            .css('position', 'absolute');

        var $container = $('<div>')
            .css('left', 0)
            .css('width', '100%')
            .css('height', 0)
            .css('position', 'relative')
            .append($element);

        // Default aspect ratio.
        if (!media || (!media.height && !media["aspect-ratio"])) {
            $container.css('padding-bottom', '56.25%');
        }

        var $widthLimiterContainer = $('<div>')
            .append($container);

        if (media) {

            if (media["aspect-ratio"]) {

                $container.css('padding-bottom', Math.round(1000 * 100 / media["aspect-ratio"]) / 1000 + '%');

                if (media["padding-bottom"]) {
                    $container.css('padding-top', media["padding-bottom"] + 'px');
                }

            } else {

                if (media.height) {
                    $container.css('height', media.height);
                }

                if (media.width) {
                    $container.css('width', media.width);
                }
            }

            // Min/max width can be controlled by one more parent div.
            if (media["max-width"] || media["min-width"]) {
                ["max-width", "min-width"].forEach(function(attr) {
                    if (media[attr]) {
                        $widthLimiterContainer.css(attr, media[attr]);
                    }
                });
            }
        }

        return $widthLimiterContainer;
    }

    var renders = {
        "javascript": {
            test: function(data) {
                return /(text|application)\/javascript/i.test(data.type)
                    && data.href;
            },
            generate: function(data) {
                return $('<script>')
                    .attr('type', data.type)
                    .attr('src', data.href);
            }
        },
        "image": {
            test: function(data) {
                return /^image(\/[\w\.-]+)?$/i.test(data.type)
                    && data.href;
            },
            generate: function(data) {
                var $img = $('<img>')
                    .attr('src', data.href);
                if (data.title) {
                    $img
                        .attr('title', data.title)
                        .attr('alt', data.title);
                }
                // TODO: add image width, heigth?
                return $img;
            }
        },
        "mp4video": {
            test: function(data) {
                return (data.type == "video/mp4"
                    || data.type == "video/webm"
                    || data.type == "video/ogg");
            },
            generate: function(data, options) {

                var iframelyData = options && options.iframelyData;

                var givf = data.rel.indexOf('gifv') > -1;
                var autoplay = data.rel.indexOf('autoplay') > -1 || givf;

                var $video = $('<video' + (givf ? ' loop muted webkit-playsinline' : ' controls') + (autoplay ? ' autoplay' : '') + '>Your browser does not support HTML5 video.</video>');

                if (iframelyData && iframelyData.links) {

                    // Find video aspect.
                    var media = data.media;
                    var aspect, width;

                    if (media) {

                        aspect = media["aspect-ratio"];

                        if (!aspect) {
                            [
                                ["min-width", "min-height"],
                                ["max-width", "max-height"],
                                ["width", "height"]
                            ].forEach(function(dims) {
                                    width = media[dims[0]];
                                    var height = media[dims[1]];
                                    if (width && height) {
                                        aspect = width / height;
                                    }
                                });
                        }
                    }

                    var hasPromo = $.iframely.filterLinks(iframelyData.links, function(link) {
                        return link.rel.indexOf('promo') > -1;
                    }).length;

                    // Find images with same aspect.
                    var thumbnails = $.iframely.filterLinks(iframelyData.links, function(link) {

                        if (hasPromo && link.rel.indexOf('promo') === -1) {
                            return;
                        }

                        if (renders["image"].test(link) && (link.rel.indexOf('thumbnail') > -1 || link.rel.indexOf('image') > -1) && link.type.indexOf('gif') === -1 && !link.href.match(/\.gif(?:[#\?].*)?$/i)) {
                            var m = link.media;
                            if (aspect && m && m.width && m.height) {
                                var imgAspect = m.width / m.height;
                                return Math.abs(imgAspect - aspect) < 0.15;
                            }
                            return true;
                        }
                    });

                    // Find largest image.
                    thumbnails.sort(function(a, b) {
                        var w1 = a.media && a.media.width;
                        var w2 = b.media && b.media.width;
                        if (w1 == w2) {
                            return 0;
                        }
                        if (w1 && w2) {
                            return w2 - w1;
                        }
                        // Images without size goes last.
                        if (!w1) {
                            return 1;
                        }
                        if (!w2) {
                            return -1;
                        }
                    });

                    if (thumbnails.length) {
                        $video.attr("poster", thumbnails[0].href);
                    }
                }

                $video.append('<source />')
                    .children('source')
                    .attr('src', data.href)
                    .attr('type', data.type);

                if (options && options.disableSizeWrapper) {
                    return $video;
                } else {
                    return wrapContainer($video, data);
                }
            }
        },
        "flash": {
            test: function(data) {
                return data.type === "application/x-shockwave-flash" && data.href;
            },
            generate: function(data, options) {

                var $embed = $('<embed>')
                    .attr('src', data.href)
                    .attr('type', 'application/x-shockwave-flash');

                if (options && options.disableSizeWrapper) {
                    return $embed;
                } else {
                    return wrapContainer($embed, data);
                }
            }
        },
        "iframe": {
            test: function(data) {
                return data.type == "text/html" && data.href;
            },
            generate: function(data, options) {

                var $iframe = $('<iframe>')
                    .attr('src', data.href)
                    .attr('frameborder', '0')
                    .attr('allowfullscreen', true)
                    .attr('webkitallowfullscreen', true)
                    .attr('mozallowfullscreen', true);

                if (options && options.disableSizeWrapper) {
                    return $iframe;
                } else {
                    return wrapContainer($iframe, data);
                }
            }
        },
        "inline": {
            test: function(data) {
                return data.type === "text/html"
                    && data.rel.indexOf('inline') > -1
                    && !data.href
                    && data.html;
            },
            generate: function(data, options) {
                var $el;
                try {
                    $el = $(data.html);
                } catch(e) {
                    $el = $('<div>').append(data.html);
                }
                return $el;
            }
        }
    };

    $.iframely.generateLinkElement = function(link, options) {

        // Backward compatibility with v.0.5.1.
        if (options && options.links) {
            options = {
                iframelyData: options
            };
        }


        for(var key in renders) {
            var render = renders[key];
            if (render.test(link)) {
                return render.generate(link, options);
            }
        }
    };

    $.iframely.findBestFittedLink = function(targetWidth, targetHeight, links) {

        var sizedLinks = $.iframely.filterLinks(links, function(link) {
            var media = link.media;
            return media && media.width && media.height;
        });

        if (sizedLinks.length == 0) {
            return firstLink(links);
        }

        var targetAspect = targetWidth / targetHeight;

        var fits = [];
        sizedLinks.forEach(function(link) {

            var width = link.media.width;
            var height = link.media.height;

            var aspect = width / height;
            var scale;

            if (aspect > targetAspect) {
                scale = width / targetWidth;
            } else {
                scale = height / targetHeight;
            }

            if (scale > 1) {
                width = width / scale;
                height = height / scale;
            }

            var diffSquare = targetWidth * targetHeight - width * height;

            fits.push({
                link: link,
                scale: scale,
                modScale: scale > 1 ? scale : 1 / scale,
                diffSquare: diffSquare
            });
        });

        // Check images smaller then screen.
        var smallFit;
        var smallFits = fits.filter(function(fit) {
            return fit.scale <= 1;
        });
        if (smallFits.length > 0) {
            smallFits.sort(function(a, b) {
                return b.scale - a.scale;
            });
            smallFit = smallFits[0];
        }

        // Check images larger then screen.
        var bigFit;
        var bigFits = fits.filter(function(fit) {
            return fit.scale > 1;
        });
        if (bigFits.length > 0) {
            bigFits.sort(function(a, b) {
                // Sort by smallest black square.
                var result = a.diffSquare - a.diffSquare;
                if (result == 0) {
                    // Sort by smallest square
                    result = a.scale - b.scale;
                }
                return result;
            });
            bigFit = bigFits[0];
        }

        // Select closest to 100% scale from smallFit and bigFit.
        var results = [smallFit, bigFit].filter(function(fit) {
            return fit;
        });
        results.sort(function(a, b) {
            return a.modScale - b.modScale;
        });

        return results[0].link;
    };

    // This not works with scaling. Not used yet.
    $.iframely.findBestSizedLink = function(targetWidth, targetHeight, links) {

        var sizedLinks = $.iframely.filterLinks(links, function(link) {
            var media = link.media;
            return media && media.width && media.height;
        });

        if (sizedLinks.length == 0) {
            return firstLink(links);
        }

        var fits = [];
        sizedLinks.forEach(function(link) {
            var width = link.media.width;
            var height = link.media.height;

            var widthDiff = width - targetWidth;
            var heightDiff = height - targetHeight;

            var overSquare = 0, lostSquare = 0;

            if (widthDiff > 0 && heightDiff > 0) {
                overSquare = width * height - targetWidth * targetHeight;
            }

            if (widthDiff < 0 && heightDiff < 0) {
                lostSquare = targetWidth * targetHeight - width * height;
            }

            if (widthDiff > 0 && heightDiff < 0) {
                overSquare = widthDiff * height;
                lostSquare = - heightDiff * targetWidth;
            }

            if (widthDiff < 0 && heightDiff > 0) {
                overSquare = heightDiff * width;
                lostSquare = - widthDiff * targetHeight;
            }

            fits.push({
                link: link,
                overSquare: overSquare,
                lostSquare: lostSquare
            });
        });

        // Full fit cases, find smalest image.
        var fullFits = fits.filter(function(fit) {
            return fit.lostSquare == 0;
        });
        if (fullFits.length > 0) {
            fullFits.sort(function(a, b) {
                return a.overSquare - b.overSquare;
            });

            return fullFits[0].link;
        }

        // Partial fit cases, find less image loss.
        var partialFits = fits.filter(function(fit) {
            return fit.lostSquare > 0;
        });
        partialFits.sort(function(a, b) {
            return a.lostSquare - b.lostSquare;
        });

        return partialFits[0].link;
    };

    $.iframely.filterLinksByRel = function(rel, links, options) {

        var options = options || {};

        if (typeof rel == "string") {
            rel = [rel];
        }

        function isHttps(href) {
            return /^(?:https:)?\/\/.+/i.test(href);
        }

        var result = $.iframely.filterLinks(links, function(link) {

            if (options.httpsOnly) {
                if (!isHttps(link.href)) {
                    return false;
                }
            }

            var found = false;
            var idx = 0;
            while (!found && idx < rel.length) {
                if (link.rel.indexOf(rel[idx]) > -1) {
                    found = true;
                }
                idx++;
            }
            return found;
        });

        if (result && options.httpsFirst) {
            result.sort(function(a, b) {
                var sa = isHttps(a.href);
                var sb = isHttps(b.href);
                if (sa == sb) {
                    return 0;
                }

                if (sa && !sb) {
                    return -1;
                } else {
                    return 1;
                }
            });
        }

        if (result && options.returnOne) {
            if (result.length > 0) {
                return result[0];
            } else {
                return null;
            }
        }

        return result;
    };

    $.iframely.filterLinks = function(links, cb) {

        if (links) {

            if (links instanceof Array) {

                return links.filter(cb);

            } else if (typeof links === 'object') {

                var result = [];

                for(var id in links) {
                    var items = links[id];
                    if (items instanceof Array) {
                        items.forEach(function(item) {
                            if (cb(item)) {
                                result.push(item);
                            }
                        });
                    }
                }

                return result;
            }
        }

        return [];
    };

    function firstLink(links) {

        if (links) {

            if (links instanceof Array) {

                if (links.length > 0) {
                    return links[0];
                } else {
                    return;
                }

            } else if (typeof links === 'object') {

                for(var id in links) {
                    var items = links[id];
                    if (items instanceof Array) {
                        if (items.length > 0) {
                            return items[0];
                        }
                    }
                }
            }
        }
    }

})( jQuery );