<commit_before>// eslint-disable-next-line no-global-assign,no-native-reassign
embedit = {};

embedit.imageTypes = {
    image: 'image',
    gfycat: 'gfycat',
    gifv: 'gifv',
    redgif: 'redgif'
};

embedit.redditBaseUrl = "http://www.reddit.com";

if (typeof window === "undefined") {
    // eslint-disable-next-line no-redeclare
    var window = {};
}

if (window.location && window.location.protocol === 'https:') {
    // page is secure
    embedit.redditBaseUrl = "https://www.reddit.com";
}

embedit.video = function (webmUrl, mp4Url) {
    // imgur is annoying and when you use the <source> tags
    // it tries to redirect you to the gifv page instead of serving
    // video. We can only circumvent that by putting the src
    // on the <video> tag :/

    var video = $('<video autoplay playsinline loop />');
    if (webmUrl) {
        video.append($('<source/>').attr('src', webmUrl));
    }
    if (mp4Url) {
        video.append($('<source/>').attr('src', mp4Url));
    }
    return video;
    //video.attr("src", urls[0]);
    /*
    var url;
    if(!webmUrl && !mp4Url) {
        console.error("Empty video urls given");
        return;
    }

    if(mp4Url)
        url = mp4Url
    else
        url = webmUrl

    if (Modernizr.video.webm && webmUrl) {
        // Thank you http://diveintohtml5.info/detect.html
        // try WebM
        url = webmUrl;
    }

    url = url.replace("http://", "https://");
    video.attr("src", url);
    return video;*/
}

embedit.unsupported = function(url) {
    console.log("Omitting unsupported url: '" + url + "'");
}

embedit.redGifConvert = function (url, embedFunc) {
    var name = embedit.redGifUrlToId(url);

    if(!name) {
        console.log("Failed to identify redgif name");
        return false;
    }

    $.ajax({
        url: 'https://api.redgifs.com/v1/gfycats/' + name,
        dataType: "json",
        success: function(data) {
            if (!data || !data.gfyItem || !data.gfyItem.content_urls.mp4) {
                console.log("gfy data missing video url");
                embedFunc(null);
                return;
            }
            embedFunc(embedit.video(null, data.gfyItem.content_urls.mp4.url));
        }
    })
    return true;
}

embedit.convertors = [
    {
        name: "imgurAlbums",
        detect: /imgur\.com\/a\/.*/,
        convert: function (url, embedFunc) {
            embedit.unsupported(url);
            embedFunc(null);
            return true;
        }
    },
    {
        name: "imgurGifv",
        detect: /imgur\.com.*(gif|gifv|mp4|webm)/,
        convert: function (url, embedFunc) {
            var no_extension = url.replace(/\.\w+$/, '')
            var webmUrl = no_extension + '.webm'
            var mp4Url = no_extension + '.mp4';
            embedFunc(embedit.video(webmUrl, mp4Url));
            return true;
        }
    },
    {
        name: "imgurNoExtension",
        detect: /imgur\.com[^.]+/,
        convert: function (url, embedFunc) {
            var newUrl = url + '.jpg';
            var image = $('<img />');
            image.attr("src", newUrl);
            embedFunc(image);
            return true;
        }
    },
    {
        name: "redditPost",
        detect: /reddit\.com\/r\/.*/,
        convert: function (/*url*/) {
            //embedit.unsupported(url);
            return false;
        }
    },
    {
        name: "gfycat",
        detect: /gfycat\.com.*/,
        convert: function (url, embedFunc) {
            var name = embedit.gfyUrlToId(url);
            if(!name)
                return false;

            $.ajax({
                //url: 'https://gfycat.com/cajax/get/' + name,
                url: 'https://api.gfycat.com/v1/gfycats/' + name,
                dataType: "json",
                success: function(data) {
                    if (!data || !data.gfyItem || !data.gfyItem.webmUrl) {
                        embedFunc(null);
                        return;
                    }
                    embedFunc(embedit.video(data.gfyItem.webmUrl, data.gfyItem.mp4Url));
                },
                error: function() {
                    var newUrl = url.replace("gfycat.com/", "redgifs.com/watch/");
                    console.log("gfycat failed load, trying redgif", newUrl);
                    embedit.redGifConvert(newUrl, embedFunc);
                }
            })
            return true;
        },
    },
    {
        name: "redgifs",
        detect: /redgifs\.com.*/,
        convert: embedit.redGifConvert,
    },
    {
        name: "v.reddit",
        detect: /v\.redd\.it.*/,
        convert: function (url, embedFunc) {
            //var url = url + '/HLSPlaylist.m3u8';
            //var url = url + '/DASH_4_8_M?source=fallback';
            // NOTE - this step relies on the fallback_rul we get from reddit in script.js.
            // For embedit to support v.redd.it URLS on its own there will need to be something more clever here.
            embedFunc(embedit.video(url));
            return true;

            //} else if (pic.url.indexOf('//v.redd.it/') >= 0) {
            //pic.type = imageTypes.gifv;
            //pic.url = pic.url + '/HLSPlaylist.m3u8';

        },
    },
    {
        name: "imageExtension",
        detect: /\.(png|jpg|jpeg|gif|bmp)$/,
        convert: function (url, embedFunc) {
            var newElem = $('<img />', {
                id: '',
                src: url,
                alt: ''
            });
            embedFunc(newElem);
            return true;
        }
    },

]

embedit.embed = function (url, embedFunc) {
    //var embedFunc = function (elem) {
    //    elem.appendTo($("#container"));
    //}
    var keys = Object.keys(embedit.convertors);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var convertor = embedit.convertors[key];
        if (url.match(convertor.detect)) {
            //console.log("Matched: " + url + "\n to - " + convertor.name);
            var handled = convertor.convert(url, embedFunc);
            //console.log(newElem);
            if (handled)
                return true;
        }
    }
    embedit.unsupported(url);
    embedFunc(null);
}

embedit.gfyUrlToId = function(url) {
    //https://gfycat.com/cajax/get/ScaryGrizzledComet
    var match = url.match(/gfycat.com\/(gifs\/detail\/)?(\w+)/i);
    if(match && match.length > 2) {
        return match[2];
    } else {
        return false;
    }
}

embedit.redGifUrlToId = function(url) {
    var matches = url.match(/redgifs.com\/watch\/([\w-]+)\/?/i);

    if (matches && matches.length > 1) {
        return matches[1];
    } else {
        return false;
    }
}

function isImageExtension(url) {
    var goodExtensions = ['.jpg', '.jpeg', '.gif', '.bmp', '.png'];
    var dotLocation = url.lastIndexOf('.');
    if (dotLocation < 0) {
        console.log("skipped no dot: " + url);
        return false;
    }
    var extension = url.substring(dotLocation);

    return goodExtensions.indexOf(extension) >= 0;
}

embedit.processRedditJson = function(data) {
    var result = {
        children: [],
        after: null,
    }

    // handle single page json
    if (data && data.length === 2 && data[0].data.children.length === 1) {
        // this means we're in single post link
        // response consists of two json objects, one for post, one for comments
        data = data[0];
    }

    //redditData = data //global for debugging data
    // NOTE: if data.data.after is null then this causes us to start
    // from the top on the next getRedditImages which is fine.
    if (data && data.data && data.data.after) {
        result.after = data.data.after;
    }

    if (data && data.data && data.data.children) {
        result.children = data.data.children;
    } else {
        // comments of e.g. a photoshopbattles post
        //children = rp.flattenRedditData(data);
        //throw new Error("Comments pages not yet supported");
    }

    if (result.children.length === 0) {
        console.log("What case is this? Does the data have any length? Is this the standard nothing found case? TODO: debug this");
        result.children = data;
    }

    return result;
}

embedit.redditItemToPic = function(item) {
    var pic = {
        url: item.data.url || item.data.link_url,
        title: item.data.title || item.data.link_title,
        over18: item.data.over_18,
        subreddit: item.data.subreddit,
        commentsLink: embedit.redditBaseUrl + item.data.permalink,
        userLink: item.data.author,
        data: item.data,
    };

    if (!embedit.transformRedditData(pic)) {
        return null;
    }

    return pic;
}

function decodeEntities(encodedString) {
    // https://stackoverflow.com/questions/44195322/a-plain-javascript-way-to-decode-html-entities-works-on-both-browsers-and-node
    var translate_re = /&(nbsp|amp|quot|lt|gt);/g;
    var translate = {
        "nbsp":" ",
        "amp" : "&",
        "quot": "\"",
        "lt"  : "<",
        "gt"  : ">"
    };
    return encodedString.replace(translate_re, function(match, entity) {
        return translate[entity];
    }).replace(/&#(\d+);/gi, function(match, numStr) {
        var num = parseInt(numStr, 10);
        return String.fromCharCode(num);
    });
}

embedit.transformRedditData = function(pic) {
    // TODO: convert this to a more functional style

    pic.type = embedit.imageTypes.image;
    // Replace HTTP with HTTPS on gfycat and imgur to avoid this:
    //      Mixed Content: The page at 'https://redditp.com/r/gifs' was loaded over HTTPS, but requested an insecure video 'http://i.imgur.com/LzsnbNU.webm'. This content should also be served over HTTPS.
    var http_prefix = 'http://';
    var https_prefix = 'https://';
    if (pic.url.indexOf('gfycat.com') >= 0) {
        pic.type = embedit.imageTypes.gfycat;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf('redgifs.com') >= 0) {
        pic.type = embedit.imageTypes.redgif;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf('//v.redd.it/') >= 0) {
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // Sadly, we have to add domains here or they get dropped in the "cannot display url" error below.
        // Need to redesign this redditp thing.
        if (pic.data.media) {
            pic.type = embedit.imageTypes.gifv;
            pic.url = pic.data.media.reddit_video.fallback_url;
        } else if (pic.data.crosspost_parent_list && pic.data.crosspost_parent_list[0].media) {
            pic.type = embedit.imageTypes.gifv;
            pic.url = pic.data.crosspost_parent_list[0].media.reddit_video.fallback_url;
        } else {
            // some crossposts don't have a pic.data.media obj?
            return false;
        }
        pic.sound = pic.url.substring(0, pic.url.lastIndexOf('/')) + "/audio";
    } else if (pic.url.search(/^http.*imgur.*gifv?$/) > -1) {
        pic.type = embedit.imageTypes.gifv;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf("reddit.com/gallery") >= 0) {
        console.log("GOTCHA!");
        if (pic.data.gallery_data && pic.data.gallery_data.items) {
            var firstItemId = pic.data.gallery_data.items[0].media_id;
            pic.url = decodeEntities(pic.data.media_metadata[firstItemId]["s"]["u"]);
            pic.type = embedit.imageTypes.image;
        }
        console.log(pic.url)
    } else if (isImageExtension(pic.url)) {
        // simple image
    } else {
        var betterUrl = tryConvertUrl(pic.url);
        if (betterUrl !== '') {
            pic.url = betterUrl;
        } else {
            if (window.debug) {
                console.log('cannot display url as image: ' + pic.url);
            }
            return false;
        }
    }

    return true;
}

var tryConvertUrl = function (url) {
    if (url.indexOf('imgur.com') > 0 || url.indexOf('/gallery/') > 0) {
        // special cases with imgur

        if (url.indexOf('gifv') >= 0) {
            if (url.indexOf('i.') === 0) {
                url = url.replace('imgur.com', 'i.imgur.com');
            }
            return url.replace('.gifv', '.gif');
        }

        if (url.indexOf('/a/') > 0 || url.indexOf('/gallery/') > 0) {
            // albums aren't supported yet
            //log('Unsupported gallery: ' + url);
            return '';
        }

        // imgur is really nice and serves the image with whatever extension
        // you give it. '.jpg' is arbitrary
        // regexp removes /r/<sub>/ prefix if it exists
        // E.g. http://imgur.com/r/aww/x9q6yW9
        return url.replace(/r\/[^ /]+\/(\w+)/, '$1') + '.jpg';
    }

    return '';
};

//////////////////////////////////////////////////////////
// Exports
//////////////////////////////////////////////////////////

function browserNodeExport(exported, name) {
    // based off of http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/
    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
        /* global module */
        module.exports = exported;
    } else {
        if (typeof define === 'function' && define.amd) {
            /* global define */
            define([], function () {
                return exported;
            });
        } else {
            window[name] = exported;
        }
    }
}

browserNodeExport(embedit, 'embedit');
<commit_msg>Fixing bug with encodedString is undefined (#126)<commit_after>// eslint-disable-next-line no-global-assign,no-native-reassign
embedit = {};

embedit.imageTypes = {
    image: 'image',
    gfycat: 'gfycat',
    gifv: 'gifv',
    redgif: 'redgif'
};

embedit.redditBaseUrl = "http://www.reddit.com";

if (typeof window === "undefined") {
    // eslint-disable-next-line no-redeclare
    var window = {};
}

if (window.location && window.location.protocol === 'https:') {
    // page is secure
    embedit.redditBaseUrl = "https://www.reddit.com";
}

embedit.video = function (webmUrl, mp4Url) {
    // imgur is annoying and when you use the <source> tags
    // it tries to redirect you to the gifv page instead of serving
    // video. We can only circumvent that by putting the src
    // on the <video> tag :/

    var video = $('<video autoplay playsinline loop />');
    if (webmUrl) {
        video.append($('<source/>').attr('src', webmUrl));
    }
    if (mp4Url) {
        video.append($('<source/>').attr('src', mp4Url));
    }
    return video;
    //video.attr("src", urls[0]);
    /*
    var url;
    if(!webmUrl && !mp4Url) {
        console.error("Empty video urls given");
        return;
    }

    if(mp4Url)
        url = mp4Url
    else
        url = webmUrl

    if (Modernizr.video.webm && webmUrl) {
        // Thank you http://diveintohtml5.info/detect.html
        // try WebM
        url = webmUrl;
    }

    url = url.replace("http://", "https://");
    video.attr("src", url);
    return video;*/
}

embedit.unsupported = function(url) {
    console.log("Omitting unsupported url: '" + url + "'");
}

embedit.redGifConvert = function (url, embedFunc) {
    var name = embedit.redGifUrlToId(url);

    if(!name) {
        console.log("Failed to identify redgif name");
        return false;
    }

    $.ajax({
        url: 'https://api.redgifs.com/v1/gfycats/' + name,
        dataType: "json",
        success: function(data) {
            if (!data || !data.gfyItem || !data.gfyItem.content_urls.mp4) {
                console.log("gfy data missing video url");
                embedFunc(null);
                return;
            }
            embedFunc(embedit.video(null, data.gfyItem.content_urls.mp4.url));
        }
    })
    return true;
}

embedit.convertors = [
    {
        name: "imgurAlbums",
        detect: /imgur\.com\/a\/.*/,
        convert: function (url, embedFunc) {
            embedit.unsupported(url);
            embedFunc(null);
            return true;
        }
    },
    {
        name: "imgurGifv",
        detect: /imgur\.com.*(gif|gifv|mp4|webm)/,
        convert: function (url, embedFunc) {
            var no_extension = url.replace(/\.\w+$/, '')
            var webmUrl = no_extension + '.webm'
            var mp4Url = no_extension + '.mp4';
            embedFunc(embedit.video(webmUrl, mp4Url));
            return true;
        }
    },
    {
        name: "imgurNoExtension",
        detect: /imgur\.com[^.]+/,
        convert: function (url, embedFunc) {
            var newUrl = url + '.jpg';
            var image = $('<img />');
            image.attr("src", newUrl);
            embedFunc(image);
            return true;
        }
    },
    {
        name: "redditPost",
        detect: /reddit\.com\/r\/.*/,
        convert: function (/*url*/) {
            //embedit.unsupported(url);
            return false;
        }
    },
    {
        name: "gfycat",
        detect: /gfycat\.com.*/,
        convert: function (url, embedFunc) {
            var name = embedit.gfyUrlToId(url);
            if(!name)
                return false;

            $.ajax({
                //url: 'https://gfycat.com/cajax/get/' + name,
                url: 'https://api.gfycat.com/v1/gfycats/' + name,
                dataType: "json",
                success: function(data) {
                    if (!data || !data.gfyItem || !data.gfyItem.webmUrl) {
                        embedFunc(null);
                        return;
                    }
                    embedFunc(embedit.video(data.gfyItem.webmUrl, data.gfyItem.mp4Url));
                },
                error: function() {
                    var newUrl = url.replace("gfycat.com/", "redgifs.com/watch/");
                    console.log("gfycat failed load, trying redgif", newUrl);
                    embedit.redGifConvert(newUrl, embedFunc);
                }
            })
            return true;
        },
    },
    {
        name: "redgifs",
        detect: /redgifs\.com.*/,
        convert: embedit.redGifConvert,
    },
    {
        name: "v.reddit",
        detect: /v\.redd\.it.*/,
        convert: function (url, embedFunc) {
            //var url = url + '/HLSPlaylist.m3u8';
            //var url = url + '/DASH_4_8_M?source=fallback';
            // NOTE - this step relies on the fallback_rul we get from reddit in script.js.
            // For embedit to support v.redd.it URLS on its own there will need to be something more clever here.
            embedFunc(embedit.video(url));
            return true;

            //} else if (pic.url.indexOf('//v.redd.it/') >= 0) {
            //pic.type = imageTypes.gifv;
            //pic.url = pic.url + '/HLSPlaylist.m3u8';

        },
    },
    {
        name: "imageExtension",
        detect: /\.(png|jpg|jpeg|gif|bmp)$/,
        convert: function (url, embedFunc) {
            var newElem = $('<img />', {
                id: '',
                src: url,
                alt: ''
            });
            embedFunc(newElem);
            return true;
        }
    },
    {
        name: "preview.redd.it",
        detect: /preview\.redd\.it.*/,
        convert: function (url, embedFunc) {
            embedFunc(embedit.video(url));
            return true;
		}
    }

]

embedit.embed = function (url, embedFunc) {
    //var embedFunc = function (elem) {
    //    elem.appendTo($("#container"));
    //}
    var keys = Object.keys(embedit.convertors);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var convertor = embedit.convertors[key];
        if (url.match(convertor.detect)) {
            //console.log("Matched: " + url + "\n to - " + convertor.name);
            var handled = convertor.convert(url, embedFunc);
            //console.log(newElem);
            if (handled)
                return true;
        }
    }
    embedit.unsupported(url);
    embedFunc(null);
}

embedit.gfyUrlToId = function(url) {
    //https://gfycat.com/cajax/get/ScaryGrizzledComet
    var match = url.match(/gfycat.com\/(gifs\/detail\/)?(\w+)/i);
    if(match && match.length > 2) {
        return match[2];
    } else {
        return false;
    }
}

embedit.redGifUrlToId = function(url) {
    var matches = url.match(/redgifs.com\/watch\/([\w-]+)\/?/i);

    if (matches && matches.length > 1) {
        return matches[1];
    } else {
        return false;
    }
}

function isImageExtension(url) {
    var goodExtensions = ['.jpg', '.jpeg', '.gif', '.bmp', '.png'];
    var dotLocation = url.lastIndexOf('.');
    if (dotLocation < 0) {
        console.log("skipped no dot: " + url);
        return false;
    }
    var extension = url.substring(dotLocation);

    return goodExtensions.indexOf(extension) >= 0;
}

embedit.processRedditJson = function(data) {
    var result = {
        children: [],
        after: null,
    }

    // handle single page json
    if (data && data.length === 2 && data[0].data.children.length === 1) {
        // this means we're in single post link
        // response consists of two json objects, one for post, one for comments
        data = data[0];
    }

    //redditData = data //global for debugging data
    // NOTE: if data.data.after is null then this causes us to start
    // from the top on the next getRedditImages which is fine.
    if (data && data.data && data.data.after) {
        result.after = data.data.after;
    }

    if (data && data.data && data.data.children) {
        result.children = data.data.children;
    } else {
        // comments of e.g. a photoshopbattles post
        //children = rp.flattenRedditData(data);
        //throw new Error("Comments pages not yet supported");
    }

    if (result.children.length === 0) {
        console.log("What case is this? Does the data have any length? Is this the standard nothing found case? TODO: debug this");
        result.children = data;
    }

    return result;
}

embedit.redditItemToPic = function(item) {
    var pic = {
        url: item.data.url || item.data.link_url,
        title: item.data.title || item.data.link_title,
        over18: item.data.over_18,
        subreddit: item.data.subreddit,
        commentsLink: embedit.redditBaseUrl + item.data.permalink,
        userLink: item.data.author,
        data: item.data,
    };

    if (!embedit.transformRedditData(pic)) {
        return null;
    }

    return pic;
}

function decodeEntities(encodedString) {
    // https://stackoverflow.com/questions/44195322/a-plain-javascript-way-to-decode-html-entities-works-on-both-browsers-and-node
    var translate_re = /&(nbsp|amp|quot|lt|gt);/g;
    var translate = {
        "nbsp":" ",
        "amp" : "&",
        "quot": "\"",
        "lt"  : "<",
        "gt"  : ">"
    };
    return encodedString.replace(translate_re, function(match, entity) {
        return translate[entity];
    }).replace(/&#(\d+);/gi, function(match, numStr) {
        var num = parseInt(numStr, 10);
        return String.fromCharCode(num);
    });
}

embedit.transformRedditData = function(pic) {
    // TODO: convert this to a more functional style

    pic.type = embedit.imageTypes.image;
    // Replace HTTP with HTTPS on gfycat and imgur to avoid this:
    //      Mixed Content: The page at 'https://redditp.com/r/gifs' was loaded over HTTPS, but requested an insecure video 'http://i.imgur.com/LzsnbNU.webm'. This content should also be served over HTTPS.
    var http_prefix = 'http://';
    var https_prefix = 'https://';
    if (pic.url.indexOf('gfycat.com') >= 0) {
        pic.type = embedit.imageTypes.gfycat;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf('redgifs.com') >= 0) {
        pic.type = embedit.imageTypes.redgif;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf('//v.redd.it/') >= 0) {
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // NOTE DO NOT ADD DOMAINS HERE - MODIFY EMBEDIT.JS instead
        // Sadly, we have to add domains here or they get dropped in the "cannot display url" error below.
        // Need to redesign this redditp thing.
        if (pic.data.media) {
            pic.type = embedit.imageTypes.gifv;
            pic.url = pic.data.media.reddit_video.fallback_url;
        } else if (pic.data.crosspost_parent_list && pic.data.crosspost_parent_list[0].media) {
            pic.type = embedit.imageTypes.gifv;
            pic.url = pic.data.crosspost_parent_list[0].media.reddit_video.fallback_url;
        } else {
            // some crossposts don't have a pic.data.media obj?
            return false;
        }
        pic.sound = pic.url.substring(0, pic.url.lastIndexOf('/')) + "/audio";
    } else if (pic.url.search(/^http.*imgur.*gifv?$/) > -1) {
        pic.type = embedit.imageTypes.gifv;
        pic.url = pic.url.replace(http_prefix, https_prefix);
    } else if (pic.url.indexOf("reddit.com/gallery") >= 0) {
        console.log("GOTCHA!");
        if (pic.data.gallery_data && pic.data.gallery_data.items) {
            var firstItemId = pic.data.gallery_data.items[0].media_id;
            var encodedUrl = pic.data.media_metadata[firstItemId]["s"]["u"];
            pic.type = embedit.imageTypes.image;
            if (encodedUrl === undefined) {
                // some posts don't have the u key, but have gif and mp4 keys
                encodedUrl = pic.data.media_metadata[firstItemId]["s"]["mp4"];
                pic.type = embedit.imageTypes.gifv;
            }
            pic.url = decodeEntities(encodedUrl);
        }
        console.log(pic.url)
    } else if (isImageExtension(pic.url)) {
        // simple image
    } else {
        var betterUrl = tryConvertUrl(pic.url);
        if (betterUrl !== '') {
            pic.url = betterUrl;
        } else {
            if (window.debug) {
                console.log('cannot display url as image: ' + pic.url);
            }
            return false;
        }
    }

    return true;
}

var tryConvertUrl = function (url) {
    if (url.indexOf('imgur.com') > 0 || url.indexOf('/gallery/') > 0) {
        // special cases with imgur

        if (url.indexOf('gifv') >= 0) {
            if (url.indexOf('i.') === 0) {
                url = url.replace('imgur.com', 'i.imgur.com');
            }
            return url.replace('.gifv', '.gif');
        }

        if (url.indexOf('/a/') > 0 || url.indexOf('/gallery/') > 0) {
            // albums aren't supported yet
            //log('Unsupported gallery: ' + url);
            return '';
        }

        // imgur is really nice and serves the image with whatever extension
        // you give it. '.jpg' is arbitrary
        // regexp removes /r/<sub>/ prefix if it exists
        // E.g. http://imgur.com/r/aww/x9q6yW9
        return url.replace(/r\/[^ /]+\/(\w+)/, '$1') + '.jpg';
    }

    return '';
};

//////////////////////////////////////////////////////////
// Exports
//////////////////////////////////////////////////////////

function browserNodeExport(exported, name) {
    // based off of http://www.matteoagosti.com/blog/2013/02/24/writing-javascript-modules-for-both-browser-and-node/
    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
        /* global module */
        module.exports = exported;
    } else {
        if (typeof define === 'function' && define.amd) {
            /* global define */
            define([], function () {
                return exported;
            });
        } else {
            window[name] = exported;
        }
    }
}

browserNodeExport(embedit, 'embedit');
<|endoftext|>


<commit_before>function rotate3(list, amt) {
    switch (amt%3) {
        case 0: return list;
        case 1: return [list[2], list[0], list[1]];
        case 2: return [list[1], list[2], list[0]];
    }
}

function hsv2rgb(h, s, v) {
    if (h < 0) {
        h = (h%360)+360
    }

    var fac = (h%120) / 60;
    var out = (fac < 1) ? [v, (1-(1-fac)*s)*v, (1-s)*v] : [(1-(fac-1)*s), v, (1-s)*v];
    out = rotate3(out, Math.floor(h%360 / 120));

    return out;
}

class CanvasWrapper {
    constructor() {
        this.canvas = document.getElementById('world');
        this.ctx = this.canvas.getContext('2d');
        this.onClick = null;
        this.mouseOver = false;
        this.mousePos = [0,0];

        this.updateMetrics();

        // Event listeners
        var that = this;

        this.canvas.addEventListener("click", function(e) {
            var x = e.pageX - e.target.offsetLeft;
            var y = e.pageY - e.target.offsetTop;

            if (that.onClick) {
                that.onClick(that.fromScreenX(x), that.fromScreenY(y));
            }
        })

        this.canvas.addEventListener("mousemove", function(e) {
            var x = e.pageX - e.target.offsetLeft;
            var y = e.pageY - e.target.offsetTop;

            that.mousePos = [that.fromScreenX(x), that.fromScreenY(y)];
        })

        this.canvas.addEventListener("mouseenter", function(e) {
            that.mouseOver = true;
        })

        this.canvas.addEventListener("mouseleave", function(e) {
            that.mouseOver = false;
        })
    }
    updateMetrics() {
        this.width = this.canvas.offsetWidth;
        this.height = this.canvas.offsetHeight;
        this.offsetx = this.width/2;
        this.offsety = this.height/2;
        this.scale = 1;
    }
    // Coordinate transformations
    toScreenX(val) {return (this.offsetx + val) * this.scale}
    toScreenY(val) {return (this.offsety - val) * this.scale}
    toScreenScale(val) {return val * this.scale}
    fromScreenX(val) {return val / this.scale - this.offsetx}
    fromScreenY(val) {return this.offsety - val / this.scale}
    fromScreenScale(val) {return val / this.scale}

    // Graphics
    fill(color) {
        this.ctx.fillStyle = `rgb(${Math.round(color[0]*255)},${Math.round(color[1]*255)},${Math.round(color[2]*255)})`;
        this.ctx.fillRect(0,0,this.width,this.height);
    }
    fillCircle(color, radius, x, y) {
        this.ctx.beginPath();
        this.ctx.fillStyle = `rgb(${Math.round(color[0]*255)},${Math.round(color[1]*255)},${Math.round(color[2]*255)})`;
        this.ctx.arc(this.toScreenX(x), this.toScreenY(y), this.toScreenScale(radius), 0, Math.PI*2);
        this.ctx.closePath();
        this.ctx.fill();
    }

}

class Board {
    constructor() {
        this.params = {
            creatureAwareness: 10,
            creatureSpeed: 1/20,
            movementCost: 1/10000,
            eatSpeed: 5,
            drag: 15/20
        }
        this.creatures = [];
    }

    sense(creature, radius, rotate) {

        var c = {r:0, g:0, b:0};
        return {up:c, left:c, down:c, right:c, center:c};
    }
    tick() {
        this.creatures.forEach(c=>c.think());
        this.creatures.forEach(c=>c.update());
        for (var i = 0; i < this.creatures.length - 1; i++) {
            var c1 = this.creatures[i];
            for (var k = i+1; k < this.creatures.length; k++) {
                var c2 = this.creatures[k];
                var dist = Math.sqrt(Math.pow(c1.x - c2.x,2) + Math.pow(c1.y - c2.y,2));
                var collision = (dist < c1.radius + c2.radius);

                if (collision) {
                    c1.collide(c2) || c2.collide(c1)
                }
            }
        }
        this.creatures = this.creatures.filter(c=>c.energy > 0);
    }
    draw(canvas) {
        canvas.fill([0.1,0.1,0.1]);
        this.creatures.forEach(c=>{
            c.draw(canvas);
        })
    }
}

class Thing {
    constructor(board, x, y) {
        this.radius = 1;
        this.energy = 1;
        this.color = [1,1,1];
        this.x = x;
        this.y = y;
        this.vx = 0;
        this.vy = 0;
        this.board = board;

        board.creatures.push(this);
    }
    collide(other) {
        return false;
    }
    draw() {}
}

class Creature extends Thing {
    constructor(board, x, y, mind, type, energy) {
        super(board, x, y);
        this.mind = mind;
        this.type = type;
        this.energy = energy;
    }
    think() {
        var sensors = this.board.sense(this, this.board.params.creatureAwareness, this.type)
        this.thoughts = this.mind.think(this.energy, this.vx, this.vy, sensors, this);

        this.thoughts.moveX = +this.thoughts.moveX || 0;
        this.thoughts.moveY = +this.thoughts.moveY || 0;
    }
    update() {
        this.vx += +this.thoughts.moveX * this.board.params.creatureSpeed;
        this.x += this.vx;
        this.vx *= this.board.params.drag;

        this.vy += +this.thoughts.moveY * this.board.params.creatureSpeed;
        this.y += this.vy;
        this.vy *= this.board.params.drag;

        this.radius = Math.sqrt(this.energy);

        this.color = hsv2rgb(this.thoughts.hue + this.type * 120, this.thoughts.sat, this.thoughts.val);

        this.energy -= (Math.pow(this.thoughts.moveX, 2) + Math.pow(this.thoughts.moveY, 2)) * this.board.params.movementCost
    }
    collide(other) {
        if (other instanceof Creature) {
            if ((this.type - other.type + 3) % 3 == 2) {
                // Om nom nom
                var amt = Math.min(other.energy, board.params.eatSpeed);
                other.energy -= amt;
                this.energy += amt;
                return true;
            }
        }
    }
    draw(canvas) {
        canvas.fillCircle(this.color, this.radius, this.x, this.y);
    }
}

class DummyMind {
    constructor() {}
    think(energy, vx, vy, sensors) {
        return {
            moveX: 0,
            moveY: 0,
            hue: 0,
            sat: 1,
            val: 1,
            respawn: 0
        }
    }
}

class FollowMind {
    constructor() {}
    think(energy, vx, vy, sensors, creature) {
        var ax = 0, ay = 0;

        if (canvas.mouseOver) {
            ax = canvas.mousePos[0] - creature.x;
            ay = canvas.mousePos[1] - creature.y;
        }
        return {
            moveX: ax,
            moveY: ay,
            hue: Math.atan2(ay, ax) * 180 / Math.PI,
            sat: 1,
            val: 1,
            respawn: 0
        }
    }
}

var board;
var canvas;
var repeatingTick;
var minds = {dummy:_=>new DummyMind(), follow:_=>new FollowMind()}

function init() {
    board = new Board();
    board.canvas = new CanvasWrapper();
    canvas = new CanvasWrapper();
    repeatingTick = setInterval(tick, 50);

    canvas.onClick = function(x,y) {
        type = +document.getElementById("type").value;
        mind =  document.getElementById("mind").value;
        new Creature(board, x, y, minds[mind](), type, 100);
    }
}

function tick() {
    board.tick();
    board.draw(canvas);
    document.getElementById("energy-display").innerText = (board.creatures.reduce((s,c)=>s+c.energy, 0))
}<commit_msg>added splitting, board walls, camera scaling<commit_after>function rotate3(list, amt) {
    switch (amt%3) {
        case 0: return list;
        case 1: return [list[2], list[0], list[1]];
        case 2: return [list[1], list[2], list[0]];
    }
}

function hsv2rgb(h, s, v) {
    if (h < 0) {
        h = (h%360)+360
    }

    var fac = (h%120) / 60;
    var out = (fac < 1) ? [v, (1-(1-fac)*s)*v, (1-s)*v] : [(1-(fac-1)*s), v, (1-s)*v];
    out = rotate3(out, Math.floor(h%360 / 120));

    return out;
}

class CanvasWrapper {
    constructor() {
        this.canvas = document.getElementById('world');
        this.ctx = this.canvas.getContext('2d');
        this.onClick = null;
        this.mouseOver = false;
        this.mousePos = [0,0];

        this.updateMetrics();

        // Event listeners
        var that = this;

        this.canvas.addEventListener("click", function(e) {
            var x = e.pageX - e.target.offsetLeft;
            var y = e.pageY - e.target.offsetTop;

            if (that.onClick) {
                that.onClick(that.fromScreenX(x), that.fromScreenY(y));
            }
        })

        this.canvas.addEventListener("mousemove", function(e) {
            var x = e.pageX - e.target.offsetLeft;
            var y = e.pageY - e.target.offsetTop;

            that.mousePos = [that.fromScreenX(x), that.fromScreenY(y)];
        })

        this.canvas.addEventListener("mouseenter", function(e) {
            that.mouseOver = true;
        })

        this.canvas.addEventListener("mouseleave", function(e) {
            that.mouseOver = false;
        })
    }
    updateMetrics(board) {
        this.width = this.canvas.offsetWidth;
        this.height = this.canvas.offsetHeight;
        this.offsetx = this.width/2;
        this.offsety = this.height/2;
        if (board) {
            this.scale = Math.min(this.width / board.width, this.height / board.height);
        }
    }
    // Coordinate transformations
    toScreenX(val) {return this.offsetx + val * this.scale}
    toScreenY(val) {return this.offsety + val * this.scale}
    toScreenScale(val) {return val * this.scale}
    fromScreenX(val) {return (val - this.offsetx) / this.scale}
    fromScreenY(val) {return (val - this.offsety) / this.scale}
    fromScreenScale(val) {return val / this.scale}

    // Graphics
    _setFill(color) {
        this.ctx.fillStyle = `rgb(${Math.round(color[0]*255)},${Math.round(color[1]*255)},${Math.round(color[2]*255)})`;
    }
    _setStroke(color) {
        this.ctx.strokeStyle = `rgb(${Math.round(color[0]*255)},${Math.round(color[1]*255)},${Math.round(color[2]*255)})`;
    }
    fill(color) {
        this._setFill(color);
        this.ctx.fillRect(0,0,this.width,this.height);
    }
    fillCircle(color, radius, x, y) {
        this._setFill(color);
        this.ctx.beginPath();
        this.ctx.arc(this.toScreenX(x), this.toScreenY(y), this.toScreenScale(radius), 0, Math.PI*2);
        this.ctx.closePath();
        this.ctx.fill();
    }
    strokeRect(color, thickness, x, y, w, h) {
        this._setStroke(color);
        this.ctx.lineWidth = this.toScreenScale(thickness);
        this.ctx.strokeRect(this.toScreenX(x), this.toScreenY(y), this.toScreenScale(w), this.toScreenScale(h));
    }
}

class Board {
    constructor(width, height) {
        this.width = width;
        this.height = height;
        this.params = {
            creatureAwareness: 10,
            creatureSpeed: 1/20,
            movementCost: 1/10000,
            eatSpeed: 5,
            drag: 15/20
        }
        this.creatures = [];
    }

    sense(creature, radius, rotate) {

        var c = {r:0, g:0, b:0};
        return {up:c, left:c, down:c, right:c, center:c};
    }
    tick() {
        this.creatures.forEach(c=>c.think());
        this.creatures.forEach(c=>c.update());
        for (var i = 0; i < this.creatures.length - 1; i++) {
            var c1 = this.creatures[i];
            for (var k = i+1; k < this.creatures.length; k++) {
                var c2 = this.creatures[k];
                var dist = Math.sqrt(Math.pow(c1.x - c2.x,2) + Math.pow(c1.y - c2.y,2));
                var collision = (dist < c1.radius + c2.radius);

                if (collision) {
                    c1.collide(c2) || c2.collide(c1)
                }
            }
        }
        this.creatures = this.creatures.filter(c=>c.energy > 0);
    }
    draw(canvas) {
        canvas.fill([0.1,0.1,0.1]);
        this.creatures.forEach(c=>{
            c.draw(canvas);
        })
        canvas.strokeRect([0.2, 0.2, 0.2], 5, -this.width/2, -this.height/2, this.width, this.height);
    }
}

class Thing {
    constructor(board, x, y) {
        this.radius = 1;
        this.energy = 1;
        this.color = [1,1,1];
        this.x = x;
        this.y = y;
        this.vx = 0;
        this.vy = 0;
        this.board = board;

        board.creatures.push(this);
    }
    collide(other) {
        return false;
    }
    draw() {}
}

class Creature extends Thing {
    constructor(board, x, y, mind, type, energy) {
        super(board, x, y);
        this.mind = mind;
        this.type = type;
        this.energy = energy;
    }
    think() {
        var sensors = this.board.sense(this, this.board.params.creatureAwareness, this.type)
        this.thoughts = this.mind.think(this.energy, this.vx, this.vy, sensors, this);

        this.thoughts.moveX = +this.thoughts.moveX || 0;
        this.thoughts.moveY = +this.thoughts.moveY || 0;
    }
    update() {
        this.vx += +this.thoughts.moveX * this.board.params.creatureSpeed;
        this.x += this.vx;
        this.vx *= this.board.params.drag;

        this.vy += +this.thoughts.moveY * this.board.params.creatureSpeed;
        this.y += this.vy;
        this.vy *= this.board.params.drag;

        if (Math.abs(this.x) > board.width/2 - this.radius) {
            this.x = Math.sign(this.x) * (board.width/2 - this.radius)
            this.vx *= -1;
        };
        if (Math.abs(this.y) > board.height/2 - this.radius) {
            this.y = Math.sign(this.y) * (board.height/2 - this.radius)
            this.vy *= -1;
        };

        this.energy -= (Math.pow(this.thoughts.moveX, 2) + Math.pow(this.thoughts.moveY, 2)) * this.board.params.movementCost

        if (this.thoughts.split >= 1) {
            var offspring = new Creature(this.board, this.x, this.y, this.mind.newMind(), Math.floor(Math.random()*3));
            offspring.energy = this.energy / 2;
            this.energy /= 2;
        }

        this.radius = Math.sqrt(this.energy);

        this.color = hsv2rgb(this.thoughts.hue + this.type * 120, this.thoughts.sat, this.thoughts.val);
    }
    collide(other) {
        if (other instanceof Creature) {
            if ((this.type - other.type + 3) % 3 == 1) {
                // Om nom nom
                var amt = Math.min(other.energy, board.params.eatSpeed);
                other.energy -= amt;
                this.energy += amt;
                return true;
            }
        }
    }
    draw(canvas) {
        canvas.fillCircle(this.color, this.radius, this.x, this.y);
    }
}

class DummyMind {
    constructor() {}
    think(energy, vx, vy, sensors) {
        return {
            moveX: 0,
            moveY: 0,
            hue: 0,
            sat: 1,
            val: 1,
            split: 0
        }
    }
}

class FollowMind {
    constructor() {}
    think(energy, vx, vy, sensors, creature) {
        var ax = 0, ay = 0;

        if (canvas.mouseOver) {
            ax = canvas.mousePos[0] - creature.x;
            ay = canvas.mousePos[1] - creature.y;
        }
        return {
            moveX: ax,
            moveY: ay,
            hue: Math.atan2(ay, ax) * 180 / Math.PI,
            sat: 1,
            val: 1,
            split: (energy > 400 ? 1 : 0)
        }
    }
    newMind() {
        return new DummyMind();
    }
}

class SimpleMind {
    constructor() {

    }
}

var board;
var canvas;
var repeatingTick;
var minds = {dummy:_=>new DummyMind(), follow:_=>new FollowMind()}

function init() {
    board = new Board(1000,1000);
    canvas = new CanvasWrapper();
    canvas.updateMetrics(board);
    repeatingTick = setInterval(tick, 50);

    canvas.onClick = function(x,y) {
        type = +document.getElementById("type").value;
        mind =  document.getElementById("mind").value;
        new Creature(board, x, y, minds[mind](), type, 100);
    }
}

function tick() {
    board.tick();
    board.draw(canvas);
    document.getElementById("energy-display").innerText = (board.creatures.reduce((s,c)=>s+c.energy, 0))
}<|endoftext|>


<commit_before>/*!
 * Copyright 2015 mifort.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

angular.module('mifortTimesheet.employeesReport').factory('employeesReportService',
    ['$http', function ($http,$scope) {
        var self = this,
            saveFilters = [];
        self.getSavedFilters = function () {
            return  saveFilters
        };
        self.saveSavedFilters = function (filters) {
            saveFilters  = filters;
        };
        self.name = localStorage.getItem('workHours');
        self.getFilters = function (companyId) {
            return $http.get('api/v1/report/filters/' + companyId);
        };
        self.getReport = function (reportSettings) {
                return $http.post('api/v1/report/aggregation', reportSettings);
        };
        self.downloadCsv = function (reportSettings) {
            return $http.post('api/v1/report/common/download/csv', reportSettings);
        };
        self.downloadAggregationCsv = function (reportSettings) {
            return $http.post('api/v1/report/aggregation/download/csv', reportSettings);
        };
        self.downloadPdf = function (reportSettings) {
            return $http.post('api/v1/report/common/download/pdf', reportSettings);
        };
        self.downloadAggregationPdf = function (reportSettings) {
            return $http.post('api/v1/report/aggregation/download/pdf', reportSettings);
        };
        self.getHeaderTemplate = function (filterHeaderTemplate) {
            return '
' +
                '

' +
                '
' +
                '{{ col.displayName CUSTOM_FILTERS }}' +
                '' +
                ' ' +
                '' +
                '
' +
                '
' +
                ' ' +
                '
' +
                '
' +
                '
' +
                ' ' +
                '
' +
                '
' +
                '
' +
                '
' + filterHeaderTemplate + '
';
        };
        self.columns = {
            date: {
                field: 'date',
                minWidth: 168,
                width: 168,
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

'
            },
            userName: {
                field: 'userName',
                displayName: 'Employee Name',
                minWidth: 190,
                width: 190,
                headerCellClass: 'name-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
' +
                '{{row.entity.userName}}' +
                '{{row.entity.userName}}' +
                '
',
                headerCellTemplate: self.getHeaderTemplate('')
            },
            projectName: {
                field: 'projectName',
                minWidth: 169,
                width: 169,
                headerCellClass: 'name-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                headerCellTemplate: self.getHeaderTemplate('')
            },
            time: {
                field: 'time',
                displayName: 'Expected Time',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
{{row.entity[col.field]}}
'
            },
            comment: {
                field: 'comment',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableSorting: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: ''
            },
            comments: {
                field: 'comments',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableSorting: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: ''
            }

        };
        self.employeeColuns = {
            actualTime: {
                field: 'actualTime',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
{{row.workHours}}
'
            },

            status: {
                field: 'status',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                paddingRight: 0,
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
Not Ready
'
            }

        };
        self.introSteps = [
            {
                element: '#step1',
                intro: "
This is a table of all logs among the application. Each column could be sorted by clicking on column name " +
                "and each of them has filter that could be opened on the filter button next to column name.
" +
                "
User, Project and Assignment columns has dropdown filter with the quick search field and checkboxes to choose the filtered options.
",
                position: 'bottom'
            },
            {
                element: '#step2',
                intro: "
Switch tabs to change the column to be aggregated.
",
                position: 'bottom'
            },
            {
                element: '#step3',
                intro: "
Use aggregation field to set the period of time to show.
",
                position: 'bottom'
            },
            {
                element: '#print',
                intro: "
You could print or export the report by pressing the top panel buttons Print/CSV.
",
                position: 'left'
            }
        ];
        return self;
    }
    ]);<commit_msg>fixed introService for 'Emloyees Report' tab<commit_after>/*!
 * Copyright 2015 mifort.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

angular.module('mifortTimesheet.employeesReport').factory('employeesReportService',
    ['$http', function ($http,$scope) {
        var self = this,
            saveFilters = [];
        self.getSavedFilters = function () {
            return  saveFilters
        };
        self.saveSavedFilters = function (filters) {
            saveFilters  = filters;
        };
        self.name = localStorage.getItem('workHours');
        self.getFilters = function (companyId) {
            return $http.get('api/v1/report/filters/' + companyId);
        };
        self.getReport = function (reportSettings) {
                return $http.post('api/v1/report/aggregation', reportSettings);
        };
        self.downloadCsv = function (reportSettings) {
            return $http.post('api/v1/report/common/download/csv', reportSettings);
        };
        self.downloadAggregationCsv = function (reportSettings) {
            return $http.post('api/v1/report/aggregation/download/csv', reportSettings);
        };
        self.downloadPdf = function (reportSettings) {
            return $http.post('api/v1/report/common/download/pdf', reportSettings);
        };
        self.downloadAggregationPdf = function (reportSettings) {
            return $http.post('api/v1/report/aggregation/download/pdf', reportSettings);
        };
        self.getHeaderTemplate = function (filterHeaderTemplate) {
            return '
' +
                '

' +
                '
' +
                '{{ col.displayName CUSTOM_FILTERS }}' +
                '' +
                ' ' +
                '' +
                '
' +
                '
' +
                ' ' +
                '
' +
                '
' +
                '
' +
                ' ' +
                '
' +
                '
' +
                '
' +
                '
' + filterHeaderTemplate + '
';
        };
        self.columns = {
            date: {
                field: 'date',
                minWidth: 168,
                width: 168,
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

'
            },
            userName: {
                field: 'userName',
                displayName: 'Employee Name',
                minWidth: 190,
                width: 190,
                headerCellClass: 'name-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
' +
                '{{row.entity.userName}}' +
                '{{row.entity.userName}}' +
                '
',
                headerCellTemplate: self.getHeaderTemplate('')
            },
            projectName: {
                field: 'projectName',
                minWidth: 169,
                width: 169,
                headerCellClass: 'name-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                headerCellTemplate: self.getHeaderTemplate('')
            },
            time: {
                field: 'time',
                displayName: 'Expected Time',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
{{row.entity[col.field]}}
'
            },
            comment: {
                field: 'comment',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableSorting: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: ''
            },
            comments: {
                field: 'comments',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableSorting: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: ''
            }

        };
        self.employeeColuns = {
            actualTime: {
                field: 'actualTime',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
{{row.workHours}}
'
            },

            status: {
                field: 'status',
                width: 150,
                minWidth: 150,
                headerCellClass: 'time-header',
                paddingRight: 0,
                enableColumnResizing: true,
                enableColumnMenu: false,
                enableFiltering: false,
                filterHeaderTemplate: '

',
                cellTemplate: '
Not Ready
'
            }

        };
        self.introSteps = [
            {
                element: '#step1',
                intro: "
This is a table of all logs among the application. Each column could be sorted by clicking on column name " +
                "and each of them has filter that could be opened on the filter button next to column name.
" +
                "
User, Project and Assignment columns has dropdown filter with the quick search field and checkboxes to choose the filtered options.
",
                position: 'bottom'
            },
            {
                element: '#step3',
                intro: "
Use aggregation field to set the period of time to show.
",
                position: 'bottom'
            }
        ];
        return self;
    }
    ]);<|endoftext|>


<commit_before>/**
 * Passport configuration
 *
 * This is the configuration for your Passport.js setup and where you
 * define the authentication strategies you want your application to employ.
 *
 * I have tested the service with all of the providers listed below - if you
 * come across a provider that for some reason doesn't work, feel free to open
 * an issue on GitHub.
 *
 * Also, authentication scopes can be set through the `scope` property.
 *
 * For more information on the available providers, check out:
 * http://passportjs.org/guide/providers/
 */

module.exports.passport = {
    local: {
        strategy: require('passport-local').Strategy
    },

    bearer: {
        strategy: require('passport-http-bearer').Strategy
    },

    twitter: {
        name: 'Twitter',
        protocol: 'oauth',
        strategy: require('passport-twitter').Strategy,
        options: {
            consumerKey: 'your-consumer-key',
            consumerSecret: 'your-consumer-secret'
        }
    },

    github: {
        name: 'GitHub',
        protocol: 'oauth2',
        strategy: require('passport-github').Strategy,
        options: {
            clientID: 'your-client-id',
            clientSecret: 'your-client-secret'
        }
    },

    basecrm: {
        name: 'BaseCRM',
        protocol: 'oauth2',
        strategy: require('passport-basecrm').Strategy,
        options: {
            clientID: 'your-client-id',
            clientSecret: 'your-client-secret',
            callbackURL: 'http://localhost:1337/auth/basecrm/callback',
            scope: ['read', 'profile', 'write', 'sync']
        }
    },

    facebook: {
        name: 'Facebook',
        protocol: 'oauth2',
        strategy: require('passport-facebook').Strategy,
        options: {
            clientID: 'your-client-id',
            clientSecret: 'your-client-secret',
            scope: ['email'] /* email is necessary for login behavior */
        }
    },

    google: {
        name: 'Google',
        protocol: 'oauth2',
        strategy: require('passport-google-oauth').OAuth2Strategy,
        options: {
            clientID: 'your-client-id',
            clientSecret: 'your-client-secret'
        }
    },

    cas: {
        name: 'CAS',
        protocol: 'cas',
        strategy: require('passport-cas').Strategy,
        options: {
            ssoBaseURL: 'http://your-cas-url',
            serverBaseURL: 'http://localhost:1337',
            serviceURL: 'http://localhost:1337/auth/cas/callback'
        }
    }
};
<commit_msg>Unfollowing passports.js<commit_after><|endoftext|>


<commit_before>// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';

//
// Compare local API data based on object graph JSON blobs to remote (Cloud
// Datastore) data. Use count-of-entities as a proxy for "needs sync". Use
// SET_MINUS to filter out existing data and upload missing data.
//

const fs = require('fs');
const path = require('path');

global.FOAM_FLAGS = {gcloud: true};
require('../lib/confluence/set_ops.es6.js');
require('../lib/datastore/datastore_container.es6.js');
require('../lib/web_catalog/object_graph_importer.es6.js');
require('../lib/web_apis/release.es6.js');
require('../lib/web_apis/release_interface_relationship.es6.js');
require('../lib/web_apis/web_interface.es6.js');
require('foam2');

const credentials = JSON.parse(fs.readFileSync(
    path.resolve(__dirname, '../.local/credentials.json')));

const logger = global.logger =
    foam.lookup('foam.nanos.log.ConsoleLogger').create();

const datastoreCtx = global.datastoreCtx =
    foam.lookup('org.chromium.apis.web.DatastoreContainer').create({
      gcloudAuthEmail: credentials.client_email,
      gcloudAuthPrivateKey: credentials.private_key,
      gcloudProjectId: credentials.project_id,
      logger: logger,
    }).ctx;

const MDAO = foam.lookup('foam.dao.MDAO');
const Release = foam.lookup('org.chromium.apis.web.Release');
const WebInterface = foam.lookup('org.chromium.apis.web.WebInterface');
const ReleaseWebInterfaceJunction =
    foam.lookup('org.chromium.apis.web.ReleaseWebInterfaceJunction');
const localCtx = foam.__context__.createSubContext({
  releaseDAO: MDAO.create({of: Release}),
  webInterfaceDAO: MDAO.create({of: WebInterface}),
  releaseWebInterfaceJunctionDAO: MDAO.create({
    of: ReleaseWebInterfaceJunction,
  }),
  logger: logger,
});
const localImporter = global.localImporter =
    foam.lookup('org.chromium.apis.web.ObjectGraphImporter').create({
      objectGraphPath: path.resolve(__dirname, '../data/og'),
    }, localCtx);

const E = foam.lookup('foam.mlang.ExpressionsSingleton').create();

const putMissing = global.putMissing = function(local, remote) {
  return local.select(
      E.SET_MINUS(remote, foam.lookup('foam.dao.AnonymousSink').create({
        sink: {
          put: function(o) {
            logger.info('Putting missing (' + local.of.id + '): ' + o.id);
            remote.put(o);
          },
        },
      })));
};

const verify = global.verify = function(local, remote) {
  return Promise.all([local, remote]).then(function(sinks) {
    if (sinks[0].value !== sinks[1].value) {
      logger.error('Counts are different (' + local.of.id + '): ' +
          sinks[0].value + ', ' + sinks[1].value);
      return putMissing(local, remote).then(() => sinks);
    } else {
      logger.info('Counts match (' + local.of.id + ')');
      return sinks;
    }
  });
};

const imported = global.imported = localImporter.import();
global.releaseCounts =
    imported.then(() => verify(localCtx.releaseDAO, datastoreCtx.releaseDAO));
global.apiCounts = imported.then(() => verify(localCtx.webInterfaceDAO,
                                              datastoreCtx.webInterfaceDAO));
global.releaseAPICounts =
    imported.then(() => verify(localCtx.releaseWebInterfaceJunctionDAO,
                               datastoreCtx.releaseWebInterfaceJunctionDAO));
<commit_msg>Fix order-of-require()s in main/datastore_update (#79)<commit_after>// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';

//
// Compare local API data based on object graph JSON blobs to remote (Cloud
// Datastore) data. Use count-of-entities as a proxy for "needs sync". Use
// SET_MINUS to filter out existing data and upload missing data.
//

const fs = require('fs');
const path = require('path');

global.FOAM_FLAGS = {gcloud: true};
require('foam2');

require('../lib/confluence/set_ops.es6.js');
require('../lib/datastore/datastore_container.es6.js');
require('../lib/web_catalog/object_graph_importer.es6.js');
require('../lib/web_apis/release.es6.js');
require('../lib/web_apis/release_interface_relationship.es6.js');
require('../lib/web_apis/web_interface.es6.js');

const credentials = JSON.parse(fs.readFileSync(
    path.resolve(__dirname, '../.local/credentials.json')));

const logger = global.logger =
    foam.lookup('foam.nanos.log.ConsoleLogger').create();

const datastoreCtx = global.datastoreCtx =
    foam.lookup('org.chromium.apis.web.DatastoreContainer').create({
      gcloudAuthEmail: credentials.client_email,
      gcloudAuthPrivateKey: credentials.private_key,
      gcloudProjectId: credentials.project_id,
      logger: logger,
    }).ctx;

const MDAO = foam.lookup('foam.dao.MDAO');
const Release = foam.lookup('org.chromium.apis.web.Release');
const WebInterface = foam.lookup('org.chromium.apis.web.WebInterface');
const ReleaseWebInterfaceJunction =
    foam.lookup('org.chromium.apis.web.ReleaseWebInterfaceJunction');
const localCtx = foam.__context__.createSubContext({
  releaseDAO: MDAO.create({of: Release}),
  webInterfaceDAO: MDAO.create({of: WebInterface}),
  releaseWebInterfaceJunctionDAO: MDAO.create({
    of: ReleaseWebInterfaceJunction,
  }),
  logger: logger,
});
const localImporter = global.localImporter =
    foam.lookup('org.chromium.apis.web.ObjectGraphImporter').create({
      objectGraphPath: path.resolve(__dirname, '../data/og'),
    }, localCtx);

const E = foam.lookup('foam.mlang.ExpressionsSingleton').create();

const putMissing = global.putMissing = function(local, remote) {
  return local.select(
      E.SET_MINUS(remote, foam.lookup('foam.dao.AnonymousSink').create({
        sink: {
          put: function(o) {
            logger.info('Putting missing (' + local.of.id + '): ' + o.id);
            remote.put(o);
          },
        },
      })));
};

const verify = global.verify = function(local, remote) {
  return Promise.all([local, remote]).then(function(sinks) {
    if (sinks[0].value !== sinks[1].value) {
      logger.error('Counts are different (' + local.of.id + '): ' +
          sinks[0].value + ', ' + sinks[1].value);
      return putMissing(local, remote).then(() => sinks);
    } else {
      logger.info('Counts match (' + local.of.id + ')');
      return sinks;
    }
  });
};

const imported = global.imported = localImporter.import();
global.releaseCounts =
    imported.then(() => verify(localCtx.releaseDAO, datastoreCtx.releaseDAO));
global.apiCounts = imported.then(() => verify(localCtx.webInterfaceDAO,
                                              datastoreCtx.webInterfaceDAO));
global.releaseAPICounts =
    imported.then(() => verify(localCtx.releaseWebInterfaceJunctionDAO,
                               datastoreCtx.releaseWebInterfaceJunctionDAO));
<|endoftext|>


<commit_before>

const modal = function (title, message, modal_type, status, resolve_func) {
  const modal_footer = $('<div>', { class: 'modal-footer' });
  const modal_body = $('<div>', { class: 'modal-body' });
  const dfd = $.Deferred();
  let resolve;
  if (resolve_func) {
    resolve = resolve_func(dfd);
  } else {
    resolve = function () { dfd.resolve(); };
  }
  if (modal_type === 0 || modal_type === 1) {
    modal_footer.append($('<button>', { class: 'btn btn-default', 'data-dismiss': 'modal', text: 'Cancel' }));
  }
  if (message instanceof jQuery || modal_type === 1 || modal_type === 3) {
    modal_body.append($('<div>', { html: message }));
  } else {
    modal_body.append($('<div>', { text: message }));
  }
  modal_footer.append($('<button>', {
    class: 'btn btn-primary', 'data-dismiss': 'modal', text: 'OK', click: resolve,
  }));
  let additional_class = '';
  if (status === 'warning') {
    additional_class = 'bg-warning';
  } else if (status === 'danger') {
    additional_class = 'bg-danger';
  }
  const modal_base = $('<div>', { class: 'modal fade', 'data-backdrop': 'static' }).append($('<div>', { class: 'modal-dialog' }).append($('<div>', { class: 'modal-content' }).append($('<div>', { class: `modal-header ${additional_class}` }).append($('<button>', { class: 'close', 'data-dismiss': 'modal' }).append($('<span>', { 'aria-hidden': 'true', html: '×' })).append($('<span>', { class: 'sr-only', text: 'Close' }))).append($('<h4>', { class: 'modal-title', text: title }))).append(modal_body).append(modal_footer)));
  modal_base.on('hidden.bs.modal', (e) => {
    modal_base.remove();
  });
  modal_base.appendTo('body').modal('show');
  modal_base.children().draggable({
    cursor: 'move',
    containment: '.modal-backdrop',
    handle: '.modal-header',
  });
  return dfd;
};
function Confirm(title, message, status) {
  return modal(title, message, 0, status);
}
exports.Confirm = Confirm;

function Alert(title, message, status) {
  return modal(title, message, 2, status);
}
exports.Alert = Alert;

function ConfirmHTML(title, message, status) {
  return modal(title, message, 1, status);
}
exports.ConfirmHTML = ConfirmHTML;

function AlertHTML(title, message, status) {
  return modal(title, message, 3, status);
}
exports.AlertHTML = AlertHTML;

function Prompt(title, label, status) {
  const input_id = _.uniqueId('bootstrap_prompt_');
  const input = $('<form>', { class: 'form-horizontal' }).append($('<div>', { class: 'form-group' }).append($('<label>', { class: 'control-label col-sm-2', for: input_id, text: label })).append($('<div>', { class: 'col-sm-5' }).append($('<input>', { class: 'form-control', type: 'text', id: input_id }))));
  const resolve_func = function (dfd) {
    return function () {
      const text = document.getElementById(input_id).value;
      dfd.resolve(text);
    };
  };
  const dfd = modal(title, input, 1, status, resolve_func);
  input.on('keypress', (e) => {
    const ENTER = 13;
    if ((e.which && e.which === ENTER) || (e.keyCode && e.keyCode === ENTER)) {
      input.closest('.modal').modal('hide');
      resolve_func(dfd)();
      return false;
    }
    return true;
  });
  return dfd;
}
exports.Prompt = Prompt;

function AlertForAjaxStdError(callback) {
  return function (xhr) {
    const t = typeof xhr === 'string' ? xhr : xhr.responseText;
    const ex = JSON.parse(t).error;
    const dfd = Alert(ex.kind, _.escape(ex.message), 'danger');
    if (callback) {
      dfd.done(callback);
    }
  };
}
exports.AlertForAjaxStdError = AlertForAjaxStdError;
<commit_msg>Fix: [WIP] modal.jsのリファクタリング<commit_after>

const modal = (title, message, modalType, status, resolveFunc) => {
  const modalFooter = $('<div>', { class: 'modal-footer' });
  const modalBody = $('<div>', { class: 'modal-body' });
  const dfd = $.Deferred();
  let resolve;
  if (resolveFunc) {
    resolve = resolveFunc(dfd);
  } else {
    resolve = () => { dfd.resolve(); };
  }
  if (modalType === 0 || modalType === 1) {
    modalFooter.append($('<button>', { class: 'btn btn-default', 'data-dismiss': 'modal', text: 'Cancel' }));
  }
  if (message instanceof jQuery || modalType === 1 || modalType === 3) {
    modalBody.append($('<div>', { html: message }));
  } else {
    modalBody.append($('<div>', { text: message }));
  }
  modalFooter.append($('<button>', {
    class: 'btn btn-primary', 'data-dismiss': 'modal', text: 'OK', click: resolve,
  }));
  let additionalClass = '';
  if (status === 'warning') {
    additionalClass = 'bg-warning';
  } else if (status === 'danger') {
    additionalClass = 'bg-danger';
  }
  const modalBase = $('<div>', { class: 'modal fade', 'data-backdrop': 'static' }).append($('<div>', { class: 'modal-dialog' })
    .append($('<div>', { class: 'modal-content' }).append($('<div>', { class: `modal-header ${additionalClass}` })
      .append($('<button>', { class: 'close', 'data-dismiss': 'modal' }).append($('<span>', { 'aria-hidden': 'true', html: '×' }))
        .append($('<span>', { class: 'sr-only', text: 'Close' })))
      .append($('<h4>', { class: 'modal-title', text: title }))).append(modalBody).append(modalFooter)));
  modalBase.on('hidden.bs.modal', () => {
    modalBase.remove();
  });
  modalBase.appendTo('body').modal('show');
  modalBase.children().draggable({
    cursor: 'move',
    containment: '.modal-backdrop',
    handle: '.modal-header',
  });
  return dfd;
};
function Confirm(title, message, status) {
  return modal(title, message, 0, status);
}
exports.Confirm = Confirm;

function Alert(title, message, status) {
  return modal(title, message, 2, status);
}
exports.Alert = Alert;

function ConfirmHTML(title, message, status) {
  return modal(title, message, 1, status);
}
exports.ConfirmHTML = ConfirmHTML;

function AlertHTML(title, message, status) {
  return modal(title, message, 3, status);
}
exports.AlertHTML = AlertHTML;

let idCount = 1;
function Prompt(title, label, status) {
  const inputId = `bootstrap_prompt_${idCount}`;
  idCount += 1;
  const input = $('<form>', { class: 'form-horizontal' }).append($('<div>', { class: 'form-group' })
    .append($('<label>', { class: 'control-label col-sm-2', for: inputId, text: label }))
    .append($('<div>', { class: 'col-sm-5' }).append($('<input>', { class: 'form-control', type: 'text', id: inputId }))));
  const resolveFunc = dfd => () => {
    const text = document.getElementById(inputId).value;
    dfd.resolve(text);
  };
  const dfd = modal(title, input, 1, status, resolveFunc);
  input.on('keypress', (e) => {
    const ENTER = 13;
    if ((e.which && e.which === ENTER) || (e.keyCode && e.keyCode === ENTER)) {
      input.closest('.modal').modal('hide');
      resolveFunc(dfd)();
      return false;
    }
    return true;
  });
  return dfd;
}
exports.Prompt = Prompt;

function AlertForAjaxStdError(callback) {
  return (xhr) => {
    const t = typeof xhr === 'string' ? xhr : xhr.responseText;
    const ex = JSON.parse(t).error;
    const dfd = Alert(ex.kind, ex.message, 'danger');
    if (callback) {
      dfd.done(callback);
    }
  };
}
exports.AlertForAjaxStdError = AlertForAjaxStdError;
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import range from 'lodash/utility/range'
import map from 'lodash/collection/map'

import Course from './course'
import List from './list'
import MissingCourse from './missing-course'
import EmptyCourseSlot from './empty-course-slot'

// import './course-list.scss'

export default function CourseList(props) {
	let courseObjects = map(props.courses, (course, i) =>
		course.error
		?
<MissingCourse key={course.clbid} clbid={course.clbid} error={course.error} />

		:
<Course
			key={course.clbid}
			index={i}
			actions={props.actions}
			course={course}
			student={props.student}
			schedule={props.schedule}
			conflicts={props.conflicts}
		/>
)

	let emptySlots = []
	if (props.creditCount < props.availableCredits) {
		const minimumExtraCreditRange = range(Math.floor(props.creditCount), props.availableCredits)
		emptySlots = map(minimumExtraCreditRange, i => <EmptyCourseSlot key={`empty-${i}`} />)
	}

	return (
		<List className='course-list' type='plain'>
			{courseObjects}
			{emptySlots}
		</List>
	)
}
CourseList.propTypes = {
	actions: PropTypes.object.isRequired,
	availableCredits: PropTypes.number.isRequired,
	conflicts: PropTypes.array.isRequired,
	courses: PropTypes.arrayOf(PropTypes.object).isRequired,
	creditCount: PropTypes.number.isRequired,
	schedule: PropTypes.object.isRequired,
	student: PropTypes.object.isRequired,
}
<commit_msg>continue wrapping List children in
s<commit_after>import React, {PropTypes} from 'react'
import range from 'lodash/utility/range'
import map from 'lodash/collection/map'

import Course from './course'
import List from './list'
import MissingCourse from './missing-course'
import EmptyCourseSlot from './empty-course-slot'

import './course-list.scss'

export default function CourseList(props) {
	let courseObjects = map(props.courses, (course, i) =>
		course.error
		?
<MissingCourse clbid={course.clbid} error={course.error} />

		:
<Course
			index={i}
			actions={props.actions}
			course={course}
			student={props.student}
			schedule={props.schedule}
			conflicts={props.conflicts}
		/>
)

	let emptySlots = []
	if (props.creditCount < props.availableCredits) {
		const minimumExtraCreditRange = range(Math.floor(props.creditCount), props.availableCredits)
		emptySlots = map(minimumExtraCreditRange, i => <li key={`empty-${i}`}><EmptyCourseSlot />
)
	}

	return (
		<List className='course-list' type='plain'>
			{courseObjects}
			{emptySlots}
		</List>
	)
}
CourseList.propTypes = {
	actions: PropTypes.object.isRequired,
	availableCredits: PropTypes.number.isRequired,
	conflicts: PropTypes.array.isRequired,
	courses: PropTypes.arrayOf(PropTypes.object).isRequired,
	creditCount: PropTypes.number.isRequired,
	schedule: PropTypes.object.isRequired,
	student: PropTypes.object.isRequired,
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
import dis from "../../../dispatcher";
import ToastStore from "../../../stores/ToastStore";
import Modal from "../../../Modal";

export default class VerificationRequestToast extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {counter: Math.ceil(props.request.timeout / 1000)};
    }

    componentDidMount() {
        const {request} = this.props;
        if (request.timeout && request.timeout > 0) {
            this._intervalHandle = setInterval(() => {
                let {counter} = this.state;
                counter = Math.max(0, counter - 1);
                this.setState({counter});
            }, 1000);
        }
        request.on("change", this._checkRequestIsPending);
        // We should probably have a separate class managing the active verification toasts,
        // rather than monitoring this in the toast component itself, since we'll get problems
        // like the toasdt not going away when the verification is cancelled unless it's the
        // one on the top (ie. the one that's mounted).
        // As a quick & dirty fix, check the toast is still relevant when it mounts (this prevents
        // a toast hanging around after logging in if you did a verification as part of login).
        this._checkRequestIsPending();
    }

    componentWillUnmount() {
        clearInterval(this._intervalHandle);
        const {request} = this.props;
        request.off("change", this._checkRequestIsPending);
    }

    _checkRequestIsPending = () => {
        const {request} = this.props;
        if (!request.canAccept) {
            ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        }
    };

    cancel = () => {
        ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        try {
            this.props.request.cancel();
        } catch (err) {
            console.error("Error while cancelling verification request", err);
        }
    }

    accept = async () => {
        ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        const {request} = this.props;
        // no room id for to_device requests
        const cli = MatrixClientPeg.get();
        try {
            await request.accept();
            if (request.channel.roomId) {
                dis.dispatch({
                    action: 'view_room',
                    room_id: request.channel.roomId,
                    should_peek: false,
                });
                dis.dispatch({
                    action: "set_right_panel_phase",
                    phase: RIGHT_PANEL_PHASES.EncryptionPanel,
                    refireParams: {
                        verificationRequest: request,
                        member: cli.getUser(request.otherUserId),
                    },
                });
            } else {
                const VerificationRequestDialog = sdk.getComponent("views.dialogs.VerificationRequestDialog");
                Modal.createTrackedDialog('Incoming Verification', '', VerificationRequestDialog, {
                    verificationRequest: request,
                }, null, /* priority = */ false, /* static = */ true);
            }
        } catch (err) {
            console.error(err.message);
        }
    };

    render() {
        const FormButton = sdk.getComponent("elements.FormButton");
        const {request} = this.props;
        const userId = request.otherUserId;
        const roomId = request.channel.roomId;
        let nameLabel = roomId ? userLabelForEventRoom(userId, roomId) : userId;
        // for legacy to_device verification requests
        if (nameLabel === userId) {
            const client = MatrixClientPeg.get();
            const user = client.getUser(userId);
            if (user && user.displayName) {
                nameLabel = _t("%(name)s (%(userId)s)", {name: user.displayName, userId});
            }
        }
        const declineLabel = this.state.counter == 0 ?
            _t("Decline") :
            _t("Decline (%(counter)s)", {counter: this.state.counter});
        return (

{nameLabel}

                <FormButton label={declineLabel} kind="danger" onClick={this.cancel} />
                <FormButton label={_t("Accept")} onClick={this.accept} />

);
    }
}

VerificationRequestToast.propTypes = {
    request: PropTypes.object.isRequired,
    toastKey: PropTypes.string.isRequired,
};
<commit_msg>show right panel before waiting for .ready event to send<commit_after>/*
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
import dis from "../../../dispatcher";
import ToastStore from "../../../stores/ToastStore";
import Modal from "../../../Modal";

export default class VerificationRequestToast extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {counter: Math.ceil(props.request.timeout / 1000)};
    }

    componentDidMount() {
        const {request} = this.props;
        if (request.timeout && request.timeout > 0) {
            this._intervalHandle = setInterval(() => {
                let {counter} = this.state;
                counter = Math.max(0, counter - 1);
                this.setState({counter});
            }, 1000);
        }
        request.on("change", this._checkRequestIsPending);
        // We should probably have a separate class managing the active verification toasts,
        // rather than monitoring this in the toast component itself, since we'll get problems
        // like the toasdt not going away when the verification is cancelled unless it's the
        // one on the top (ie. the one that's mounted).
        // As a quick & dirty fix, check the toast is still relevant when it mounts (this prevents
        // a toast hanging around after logging in if you did a verification as part of login).
        this._checkRequestIsPending();
    }

    componentWillUnmount() {
        clearInterval(this._intervalHandle);
        const {request} = this.props;
        request.off("change", this._checkRequestIsPending);
    }

    _checkRequestIsPending = () => {
        const {request} = this.props;
        if (!request.canAccept) {
            ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        }
    };

    cancel = () => {
        ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        try {
            this.props.request.cancel();
        } catch (err) {
            console.error("Error while cancelling verification request", err);
        }
    }

    accept = async () => {
        ToastStore.sharedInstance().dismissToast(this.props.toastKey);
        const {request} = this.props;
        // no room id for to_device requests
        const cli = MatrixClientPeg.get();
        try {
            if (request.channel.roomId) {
                dis.dispatch({
                    action: 'view_room',
                    room_id: request.channel.roomId,
                    should_peek: false,
                });
                dis.dispatch({
                    action: "set_right_panel_phase",
                    phase: RIGHT_PANEL_PHASES.EncryptionPanel,
                    refireParams: {
                        verificationRequest: request,
                        member: cli.getUser(request.otherUserId),
                    },
                });
            } else {
                const VerificationRequestDialog = sdk.getComponent("views.dialogs.VerificationRequestDialog");
                Modal.createTrackedDialog('Incoming Verification', '', VerificationRequestDialog, {
                    verificationRequest: request,
                }, null, /* priority = */ false, /* static = */ true);
            }
            await request.accept();
        } catch (err) {
            console.error(err.message);
        }
    };

    render() {
        const FormButton = sdk.getComponent("elements.FormButton");
        const {request} = this.props;
        const userId = request.otherUserId;
        const roomId = request.channel.roomId;
        let nameLabel = roomId ? userLabelForEventRoom(userId, roomId) : userId;
        // for legacy to_device verification requests
        if (nameLabel === userId) {
            const client = MatrixClientPeg.get();
            const user = client.getUser(userId);
            if (user && user.displayName) {
                nameLabel = _t("%(name)s (%(userId)s)", {name: user.displayName, userId});
            }
        }
        const declineLabel = this.state.counter == 0 ?
            _t("Decline") :
            _t("Decline (%(counter)s)", {counter: this.state.counter});
        return (

{nameLabel}

                <FormButton label={declineLabel} kind="danger" onClick={this.cancel} />
                <FormButton label={_t("Accept")} onClick={this.accept} />

);
    }
}

VerificationRequestToast.propTypes = {
    request: PropTypes.object.isRequired,
    toastKey: PropTypes.string.isRequired,
};
<|endoftext|>


<commit_before>/*
	Yarno Boelens
	Soundboard index.js
	created on: 25-06-2017
*/

var files = [
    {
        name:'pak-ze',
        title:'Pak ze',
        author:'flokkie'
    },
    {
        name:'koekoek',
        title:'Koekoek',
        author:'flokkie'
    },
    {
        name:'twee-men',
        title:'Twee men',
        author:'flokkie'
    },
    {
        name:'verkeerde-knopje',
        title:'Verkeerde knopje',
        author:'flokkie'
    },
    {
        name:'wtf-ricardo',
        title:'WTF Ricardo?!',
        author:'flokkie'
    },
    {
        name:'jaja',
        title:'Jaja ja',
        author:'flokkie'
    },
    {
        name:'hey-klootzak',
        title:'Hey! Klootzak!',
        author:'jos'
    },
    {
        name:'kutkutkut',
        title:'Kutkutkut',
        author:'jelle'
    },
    {
        name:'zo-jammer',
        title:'Zo jammer',
        author:'yarno'
    }
];

var audio = new Audio(),
    pageBody = document.getElementById('page-body');

files.forEach(function(file){
    var p = document.createElement("p"),
        text = document.createTextNode(file.title);
    p.appendChild(text);
    var div = document.createElement("div");
    div.appendChild(p);
    div.className += "soundItem " + file.author;
    div.onclick = function(){
        play(file.name);
    };
    pageBody.appendChild(div);
});

function play(file) {
    audio.pause()
    audio = new Audio('../audio/'+file+'.ogg');
    audio.play();
}
<commit_msg>[WEB] Added recent audio files to web board<commit_after>/*
	Yarno Boelens
	Soundboard index.js
	created on: 25-06-2017
*/

var files = [
    {
        name:'pak-ze',
        title:'Pak ze',
        author:'flokkie'
    },
    {
        name:'koekoek',
        title:'Koekoek',
        author:'flokkie'
    },
    {
        name:'twee-men',
        title:'Twee men',
        author:'flokkie'
    },
    {
        name:'verkeerde-knopje',
        title:'Verkeerde knopje',
        author:'flokkie'
    },
    {
        name:'wtf-ricardo',
        title:'WTF Ricardo?!',
        author:'flokkie'
    },
    {
        name:'jaja',
        title:'Jaja ja',
        author:'flokkie'
    },
    {
        name:'are-you-aardappel',
        title:'Are you the aardappel?',
        author:'flokkie'
    },
    {
        name:'i-am-aardappel',
        title:'I am the aardappel',
        author:'flokkie'
    },
    {
        name:'hey-klootzak',
        title:'Hey! Klootzak!',
        author:'jos'
    },
    {
        name:'kutkutkut',
        title:'Kutkutkut',
        author:'jelle'
    },
    {
        name:'zo-jammer',
        title:'Zo jammer',
        author:'yarno'
    }
];

var audio = new Audio(),
    pageBody = document.getElementById('page-body');

files.forEach(function(file){
    var p = document.createElement("p"),
        text = document.createTextNode(file.title);
    p.appendChild(text);
    var div = document.createElement("div");
    div.appendChild(p);
    div.className += "soundItem " + file.author;
    div.onclick = function(){
        play(file.name);
    };
    pageBody.appendChild(div);
});

function play(file) {
    audio.pause()
    audio = new Audio('../audio/'+file+'.ogg');
    audio.play();
}
<|endoftext|>