<commit_before>// twister_formatpost.js
// 2013 Miguel Freitas
//
// Format JSON posts and DMs to HTML.

// format "userpost" to html element
// kind = "original"/"ancestor"/"descendant"
function postToElem( post, kind ) {
    /*
    "userpost" :
    {
            "n" : username,
            "k" : seq number,
            "t" : "post" / "dm" / "rt"
            "msg" : message (post/rt)
            "time" : unix utc
            "height" : best height at user
            "dm" : encrypted message (dm) -opt
            "rt" : original userpost - opt
            "sig_rt" : sig of rt - opt
            "reply" : - opt
            {
                    "n" : reference username
                    "k" : reference k
            }
    }
    "sig_userpost" : signature by userpost.n
    */

    // Obtain data from userpost
    var postJson = $.toJSON(post);
    var userpost = post["userpost"];
    if( "rt" in userpost ) {
        var rt = userpost["rt"];
        var n = rt["n"];
        var k = rt["k"];
        var t = rt["time"];
        var msg = rt["msg"];
        var content_to_rt = $.toJSON(rt);
        var content_to_sigrt = userpost["sig_rt"];
        var retweeted_by = userpost["n"];
    } else {
        var n = userpost["n"];
        var k = userpost["k"];
        var t = userpost["time"];
        var msg = userpost["msg"]
        var content_to_rt = $.toJSON(userpost);
        var content_to_sigrt = post["sig_userpost"];
        var retweeted_by = undefined;
    }

    // Now create the html elements
    var elem = $.MAL.getPostTemplate().clone(true);
    elem.removeAttr('id');
    elem.addClass(kind);
    elem.attr('data-time', t);

    var postData = elem.find(".post-data");
    postData.addClass(kind);
    postData.attr('data-userpost', postJson);
    postData.attr('data-content_to_rt', content_to_rt);
    postData.attr('data-content_to_sigrt', content_to_sigrt);
    postData.attr('data-screen-name', n);
    postData.attr('data-id', k);
    postData.attr('data-lastk', userpost["lastk"]);
    postData.attr('data-text', msg);
    if( "reply" in userpost ) {
        postData.attr('data-replied-to-screen-name', userpost["reply"]["n"]);
        postData.attr('data-replied-to-id', userpost["reply"]["k"]);

        postData.find('.post-expand').text(polyglot.t("Show conversation"));
    }

    var postInfoName = elem.find(".post-info-name");
    postInfoName.attr('href',$.MAL.userUrl(n));
    postInfoName.text(n);
    getFullname( n, postInfoName );
    elem.find(".post-info-tag").text = "@" + n;
    getAvatar( n, elem.find(".avatar") );
    elem.find(".post-info-time").text(timeGmtToText(t));
    elem.find(".post-info-time").attr("title",timeSincePost(t));

    var mentions = [];
    htmlFormatMsg( msg, elem.find(".post-text"), mentions);
    postData.attr('data-text-mentions', mentions);

    var replyTo = "";
    if( n != defaultScreenName )
        replyTo += "@" + n + " ";
    for( var i = 0; i < mentions.length; i++ ) {
        if( mentions[i] != n && mentions[i] != defaultScreenName ) {
            replyTo += "@" + mentions[i] + " ";
        }
    }
    if(!defaultScreenName)
    {
	elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("You have to log in to post replies."));
    }
    else
    {
	elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("reply_to", { fullname: replyTo })+ "...");
    }
    elem.find(".post-area-new textarea").attr("data-reply-to",replyTo);
    postData.attr("data-reply-to",replyTo);

    if( retweeted_by != undefined ) {
        elem.find(".post-context").show();
        var retweetedByElem = elem.find(".post-retransmited-by");
        retweetedByElem.attr("href", $.MAL.userUrl(retweeted_by));
        retweetedByElem.text('@'+retweeted_by);
    }

    return elem;
}

// format dmdata (returned by getdirectmsgs) to display in "snippet" per user list
function dmDataToSnippetItem(dmData, remoteUser) {
    var dmItem = $("#dm-snippet-template").clone(true);
    dmItem.removeAttr('id');
    dmItem.attr("data-dm-screen-name",remoteUser);
    dmItem.attr("data-last_id", dmData.id);
    dmItem.attr("data-time", dmData.time);

    dmItem.find(".post-info-tag").text("@" + remoteUser);
    dmItem.find("a.post-info-name").attr("href", $.MAL.userUrl(remoteUser));
    dmItem.find("a.dm-chat-link").attr("href", $.MAL.dmchatUrl(remoteUser));
    getAvatar( remoteUser, dmItem.find(".post-photo").find("img") );
    getFullname( remoteUser, dmItem.find("a.post-info-name") );
    dmItem.find(".post-text").html(escapeHtmlEntities(dmData.text));
    dmItem.find(".post-info-time").text(timeGmtToText(dmData.time));
    dmItem.find(".post-info-time").attr("title",timeSincePost(dmData.time));

    return dmItem;
}

// format dmdata (returned by getdirectmsgs) to display in conversation thread
function dmDataToConversationItem(dmData, localUser, remoteUser) {
    var classDm = dmData.fromMe ? "sent" : "received";
    var dmItem = $("#dm-chat-template").clone(true);
    dmItem.removeAttr('id');
    dmItem.addClass(classDm);
    getAvatar(dmData.fromMe ? localUser : remoteUser, dmItem.find(".post-photo").find("img") );
    dmItem.find(".post-info-time").text(timeGmtToText(dmData.time));
    dmItem.find(".post-info-time").attr("title",timeSincePost(dmData.time));
    var mentions = [];
    htmlFormatMsg( dmData.text, dmItem.find(".post-text"), mentions);
    return dmItem;
}

// convert message text to html, featuring @users and links formating.
// todo: hashtags
function htmlFormatMsg( msg, output, mentions ) {
    var tmp;
    var match = null;
    var index;
    var strUrlRegexp = "http[s]?://";
    var strEmailRegexp = "\\S+@\\S+\\.\\S+";
    var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$";
    var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")");
    var reHttp = new RegExp(strUrlRegexp);
    var reEmail = new RegExp(strEmailRegexp);
    var reSplitCounter = new RegExp(strSplitCounterR);

    msg = escapeHtmlEntities(msg);

    while( msg != undefined && msg.length ) {

        match = reAll.exec(msg);
        if( match ) {
            index = (match[0] === match[1]) ? match.index : match.index + 1;
            if( match[1] == "@" ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substr(index+1);
                var username = _extractUsername(tmp);
                if( username.length ) {
                    if( mentions.indexOf(username) < 0 )
                        mentions.push(username);
                    var userLinkTemplate = $("#msg-user-link-template").clone(true);
                    userLinkTemplate.removeAttr("id");
                    userLinkTemplate.attr("href",$.MAL.userUrl(username));
                    userLinkTemplate.text("@"+username);
                    output.append(userLinkTemplate);
                    msg = tmp.substr(String(username).length);
                    continue;
                }
                output.append('@');
                msg = tmp;
                continue;
            }

            if( reHttp.exec(match[1]) ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                var space = tmp.search(/[ \n\t]/);
                var url;
                if( space != -1 ) url = tmp.substring(0,space); else url = tmp;
                if( url.length ) {
                    msg = tmp.substr(String(url).length);
                    url = url.replace(/&/g, '&');
                    var extLinkTemplate = $("#external-page-link-template").clone(true);
                    extLinkTemplate.removeAttr("id");

                    if ($.Options.getUseProxyOpt() !== 'disable' && !$.Options.getUseProxyForImgOnlyOpt()){
                        //proxy alternatives may be added to options page...
                        if ($.Options.getUseProxyOpt() === 'ssl-proxy-my-addr') {
                            extLinkTemplate.attr('href', 'https://ssl-proxy.my-addr.org/myaddrproxy.php/' +
                                                         url.substring(0, url.indexOf(':')) +
                                                         url.substr(url.indexOf('/') + 1));
                        } else if ($.Options.getUseProxyOpt() ==='anonymouse') {
                            extLinkTemplate.attr('href', 'http://anonymouse.org/cgi-bin/anon-www.cgi/' + url);
                        }
                    } else {
                        extLinkTemplate.attr("href",url);
                    }

                    extLinkTemplate.text(url);
                    extLinkTemplate.attr("title",url);
                    output.append(extLinkTemplate);
                    continue;
                }
            }

            if( reEmail.exec(match[1]) ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                var space = tmp.search(/[ \n\t]/);
                var email;
                if( space != -1 ) email = tmp.substring(0,space); else email = tmp;
                if( email.length ) {
                    var extLinkTemplate = $("#external-page-link-template").clone(true);
                    extLinkTemplate.removeAttr("id");
                    extLinkTemplate.attr("href","mailto:" + email);
                    extLinkTemplate.text(email);
                    extLinkTemplate.attr("title",email);
                    output.append(extLinkTemplate);
                    msg = tmp.substr(String(email).length);
                    continue;
                }
            }

            if( match[1] == "#" ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substr(index+1);
                var hashtag = _extractHashtag(tmp);
                if( hashtag.length ) {
                    var hashtag_lc='';
                    for( var i = 0; i < hashtag.length; i++ ) {
                        var c = hashtag[i];
                        hashtag_lc += (c >= 'A' && c <= 'Z') ? c.toLowerCase() : c;
                    }
                    var hashtagLinkTemplate = $("#hashtag-link-template").clone(true);
                    hashtagLinkTemplate.removeAttr("id");
                    hashtagLinkTemplate.attr("href",$.MAL.hashtagUrl(hashtag_lc));
                    hashtagLinkTemplate.text("#"+hashtag);
                    output.append(hashtagLinkTemplate);
                    msg = tmp.substr(String(hashtag).length);
                    continue;
                }
                output.append('#');
                msg = tmp;
                continue;
            }

            if (reSplitCounter.exec(match[1])) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                if( tmp.length ) {
                    var splitCounter = $('');
                    splitCounter.text(tmp);
                    output.append(splitCounter);
                    msg = "";
                    continue;
                }
            }
        }

        output.append(_formatText(msg));
        msg = "";
    }
}

// internal function for htmlFormatMsg
function _formatText(msg)
{
    // TODO: add options for emotions and linefeeds
    //msg = $.emotions(msg);
    if( $.Options.getLineFeedsOpt() == "enable" )
        msg = msg.replace(/\n/g, '
');
    return msg;
}

function _extractUsername(s) {
    var username = "";
    for( var i = 0; i < s.length; i++ ) {
        var c = s.charCodeAt(i);
        if( (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
            (c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
            (c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
            c == '_'.charCodeAt(0) ) {
            username += s[i];
        } else {
            break;
        }
    }
    return username.toLowerCase();
}

// internal function for htmlFormatMsg
function _extractHashtag(s) {
    var hashtag = "";
    s = reverseHtmlEntities(s);
    for( var i = 0; i < s.length; i++ ) {
        if( " \n\t.,:/?!;'\"()[]{}*".indexOf(s[i]) < 0 ) {
            hashtag += s[i];
        } else {
            break;
        }
    }
    return hashtag;
}

function escapeHtmlEntities(str) {
    return str
                .replace(/&/g, '&')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"')
                .replace(/'/g, ''');
}

function reverseHtmlEntities(str) {
    return str
                .replace(/&/g, '&')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"')
                .replace(/'/g, "'");
}

<commit_msg>picking "replied to" data from RT content<commit_after>// twister_formatpost.js
// 2013 Miguel Freitas
//
// Format JSON posts and DMs to HTML.

// format "userpost" to html element
// kind = "original"/"ancestor"/"descendant"
function postToElem( post, kind ) {
    /*
    "userpost" :
    {
            "n" : username,
            "k" : seq number,
            "t" : "post" / "dm" / "rt"
            "msg" : message (post/rt)
            "time" : unix utc
            "height" : best height at user
            "dm" : encrypted message (dm) -opt
            "rt" : original userpost - opt
            "sig_rt" : sig of rt - opt
            "reply" : - opt
            {
                    "n" : reference username
                    "k" : reference k
            }
    }
    "sig_userpost" : signature by userpost.n
    */

    // Obtain data from userpost
    var postJson = $.toJSON(post);
    var userpost = post["userpost"];
    if( "rt" in userpost ) {
        var rt = userpost["rt"];
        var n = rt["n"];
        var k = rt["k"];
        var t = rt["time"];
        var msg = rt["msg"];
        var content_to_rt = $.toJSON(rt);
        var content_to_sigrt = userpost["sig_rt"];
        var retweeted_by = userpost["n"];
    } else {
        var n = userpost["n"];
        var k = userpost["k"];
        var t = userpost["time"];
        var msg = userpost["msg"]
        var content_to_rt = $.toJSON(userpost);
        var content_to_sigrt = post["sig_userpost"];
        var retweeted_by = undefined;
    }

    // Now create the html elements
    var elem = $.MAL.getPostTemplate().clone(true);
    elem.removeAttr('id');
    elem.addClass(kind);
    elem.attr('data-time', t);

    var postData = elem.find(".post-data");
    postData.addClass(kind);
    postData.attr('data-userpost', postJson);
    postData.attr('data-content_to_rt', content_to_rt);
    postData.attr('data-content_to_sigrt', content_to_sigrt);
    postData.attr('data-screen-name', n);
    postData.attr('data-id', k);
    postData.attr('data-lastk', userpost["lastk"]);
    postData.attr('data-text', msg);
    if( "reply" in userpost ) {
        postData.attr('data-replied-to-screen-name', userpost["reply"]["n"]);
        postData.attr('data-replied-to-id', userpost["reply"]["k"]);

        postData.find('.post-expand').text(polyglot.t("Show conversation"));
    } else if ( "rt" in userpost && "reply" in userpost["rt"] ) {
        postData.attr('data-replied-to-screen-name', userpost["rt"]["reply"]["n"]);
        postData.attr('data-replied-to-id', userpost["rt"]["reply"]["k"]);

        postData.find('.post-expand').text(polyglot.t("Show conversation"));
    }

    var postInfoName = elem.find(".post-info-name");
    postInfoName.attr('href',$.MAL.userUrl(n));
    postInfoName.text(n);
    getFullname( n, postInfoName );
    elem.find(".post-info-tag").text = "@" + n;
    getAvatar( n, elem.find(".avatar") );
    elem.find(".post-info-time").text(timeGmtToText(t));
    elem.find(".post-info-time").attr("title",timeSincePost(t));

    var mentions = [];
    htmlFormatMsg( msg, elem.find(".post-text"), mentions);
    postData.attr('data-text-mentions', mentions);

    var replyTo = "";
    if( n != defaultScreenName )
        replyTo += "@" + n + " ";
    for( var i = 0; i < mentions.length; i++ ) {
        if( mentions[i] != n && mentions[i] != defaultScreenName ) {
            replyTo += "@" + mentions[i] + " ";
        }
    }
    if(!defaultScreenName)
    {
	elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("You have to log in to post replies."));
    }
    else
    {
	elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("reply_to", { fullname: replyTo })+ "...");
    }
    elem.find(".post-area-new textarea").attr("data-reply-to",replyTo);
    postData.attr("data-reply-to",replyTo);

    if( retweeted_by != undefined ) {
        elem.find(".post-context").show();
        var retweetedByElem = elem.find(".post-retransmited-by");
        retweetedByElem.attr("href", $.MAL.userUrl(retweeted_by));
        retweetedByElem.text('@'+retweeted_by);
    }

    return elem;
}

// format dmdata (returned by getdirectmsgs) to display in "snippet" per user list
function dmDataToSnippetItem(dmData, remoteUser) {
    var dmItem = $("#dm-snippet-template").clone(true);
    dmItem.removeAttr('id');
    dmItem.attr("data-dm-screen-name",remoteUser);
    dmItem.attr("data-last_id", dmData.id);
    dmItem.attr("data-time", dmData.time);

    dmItem.find(".post-info-tag").text("@" + remoteUser);
    dmItem.find("a.post-info-name").attr("href", $.MAL.userUrl(remoteUser));
    dmItem.find("a.dm-chat-link").attr("href", $.MAL.dmchatUrl(remoteUser));
    getAvatar( remoteUser, dmItem.find(".post-photo").find("img") );
    getFullname( remoteUser, dmItem.find("a.post-info-name") );
    dmItem.find(".post-text").html(escapeHtmlEntities(dmData.text));
    dmItem.find(".post-info-time").text(timeGmtToText(dmData.time));
    dmItem.find(".post-info-time").attr("title",timeSincePost(dmData.time));

    return dmItem;
}

// format dmdata (returned by getdirectmsgs) to display in conversation thread
function dmDataToConversationItem(dmData, localUser, remoteUser) {
    var classDm = dmData.fromMe ? "sent" : "received";
    var dmItem = $("#dm-chat-template").clone(true);
    dmItem.removeAttr('id');
    dmItem.addClass(classDm);
    getAvatar(dmData.fromMe ? localUser : remoteUser, dmItem.find(".post-photo").find("img") );
    dmItem.find(".post-info-time").text(timeGmtToText(dmData.time));
    dmItem.find(".post-info-time").attr("title",timeSincePost(dmData.time));
    var mentions = [];
    htmlFormatMsg( dmData.text, dmItem.find(".post-text"), mentions);
    return dmItem;
}

// convert message text to html, featuring @users and links formating.
// todo: hashtags
function htmlFormatMsg( msg, output, mentions ) {
    var tmp;
    var match = null;
    var index;
    var strUrlRegexp = "http[s]?://";
    var strEmailRegexp = "\\S+@\\S+\\.\\S+";
    var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$";
    var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")");
    var reHttp = new RegExp(strUrlRegexp);
    var reEmail = new RegExp(strEmailRegexp);
    var reSplitCounter = new RegExp(strSplitCounterR);

    msg = escapeHtmlEntities(msg);

    while( msg != undefined && msg.length ) {

        match = reAll.exec(msg);
        if( match ) {
            index = (match[0] === match[1]) ? match.index : match.index + 1;
            if( match[1] == "@" ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substr(index+1);
                var username = _extractUsername(tmp);
                if( username.length ) {
                    if( mentions.indexOf(username) < 0 )
                        mentions.push(username);
                    var userLinkTemplate = $("#msg-user-link-template").clone(true);
                    userLinkTemplate.removeAttr("id");
                    userLinkTemplate.attr("href",$.MAL.userUrl(username));
                    userLinkTemplate.text("@"+username);
                    output.append(userLinkTemplate);
                    msg = tmp.substr(String(username).length);
                    continue;
                }
                output.append('@');
                msg = tmp;
                continue;
            }

            if( reHttp.exec(match[1]) ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                var space = tmp.search(/[ \n\t]/);
                var url;
                if( space != -1 ) url = tmp.substring(0,space); else url = tmp;
                if( url.length ) {
                    msg = tmp.substr(String(url).length);
                    url = url.replace(/&/g, '&');
                    var extLinkTemplate = $("#external-page-link-template").clone(true);
                    extLinkTemplate.removeAttr("id");

                    if ($.Options.getUseProxyOpt() !== 'disable' && !$.Options.getUseProxyForImgOnlyOpt()){
                        //proxy alternatives may be added to options page...
                        if ($.Options.getUseProxyOpt() === 'ssl-proxy-my-addr') {
                            extLinkTemplate.attr('href', 'https://ssl-proxy.my-addr.org/myaddrproxy.php/' +
                                                         url.substring(0, url.indexOf(':')) +
                                                         url.substr(url.indexOf('/') + 1));
                        } else if ($.Options.getUseProxyOpt() ==='anonymouse') {
                            extLinkTemplate.attr('href', 'http://anonymouse.org/cgi-bin/anon-www.cgi/' + url);
                        }
                    } else {
                        extLinkTemplate.attr("href",url);
                    }

                    extLinkTemplate.text(url);
                    extLinkTemplate.attr("title",url);
                    output.append(extLinkTemplate);
                    continue;
                }
            }

            if( reEmail.exec(match[1]) ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                var space = tmp.search(/[ \n\t]/);
                var email;
                if( space != -1 ) email = tmp.substring(0,space); else email = tmp;
                if( email.length ) {
                    var extLinkTemplate = $("#external-page-link-template").clone(true);
                    extLinkTemplate.removeAttr("id");
                    extLinkTemplate.attr("href","mailto:" + email);
                    extLinkTemplate.text(email);
                    extLinkTemplate.attr("title",email);
                    output.append(extLinkTemplate);
                    msg = tmp.substr(String(email).length);
                    continue;
                }
            }

            if( match[1] == "#" ) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substr(index+1);
                var hashtag = _extractHashtag(tmp);
                if( hashtag.length ) {
                    var hashtag_lc='';
                    for( var i = 0; i < hashtag.length; i++ ) {
                        var c = hashtag[i];
                        hashtag_lc += (c >= 'A' && c <= 'Z') ? c.toLowerCase() : c;
                    }
                    var hashtagLinkTemplate = $("#hashtag-link-template").clone(true);
                    hashtagLinkTemplate.removeAttr("id");
                    hashtagLinkTemplate.attr("href",$.MAL.hashtagUrl(hashtag_lc));
                    hashtagLinkTemplate.text("#"+hashtag);
                    output.append(hashtagLinkTemplate);
                    msg = tmp.substr(String(hashtag).length);
                    continue;
                }
                output.append('#');
                msg = tmp;
                continue;
            }

            if (reSplitCounter.exec(match[1])) {
                output.append(_formatText(msg.substr(0, index)));
                tmp = msg.substring(index);
                if( tmp.length ) {
                    var splitCounter = $('');
                    splitCounter.text(tmp);
                    output.append(splitCounter);
                    msg = "";
                    continue;
                }
            }
        }

        output.append(_formatText(msg));
        msg = "";
    }
}

// internal function for htmlFormatMsg
function _formatText(msg)
{
    // TODO: add options for emotions and linefeeds
    //msg = $.emotions(msg);
    if( $.Options.getLineFeedsOpt() == "enable" )
        msg = msg.replace(/\n/g, '
');
    return msg;
}

function _extractUsername(s) {
    var username = "";
    for( var i = 0; i < s.length; i++ ) {
        var c = s.charCodeAt(i);
        if( (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
            (c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
            (c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
            c == '_'.charCodeAt(0) ) {
            username += s[i];
        } else {
            break;
        }
    }
    return username.toLowerCase();
}

// internal function for htmlFormatMsg
function _extractHashtag(s) {
    var hashtag = "";
    s = reverseHtmlEntities(s);
    for( var i = 0; i < s.length; i++ ) {
        if( " \n\t.,:/?!;'\"()[]{}*".indexOf(s[i]) < 0 ) {
            hashtag += s[i];
        } else {
            break;
        }
    }
    return hashtag;
}

function escapeHtmlEntities(str) {
    return str
                .replace(/&/g, '&')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"')
                .replace(/'/g, ''');
}

function reverseHtmlEntities(str) {
    return str
                .replace(/&/g, '&')
                .replace(/</g, '<')
                .replace(/>/g, '>')
                .replace(/"/g, '"')
                .replace(/'/g, "'");
}

<|endoftext|>


<commit_before><commit_msg>trying out bcrypt-nodejs<commit_after><|endoftext|>


<commit_before>const debug = require('debug')('github-metrics:controllers:v1:project');

const ProjectService = require('../../services/v1/project');

const ReportController = {

  /**
   * @param req
   * @param res
   * @param next
   * @return {Promise.<void>}
   */
  find: async (req, res, next) => {
    debug('executing find action');

    try {
      const projects = await ProjectService.find();
      res.status(200).send(projects);
    } catch (err) {
      next(err);
    }
  },

  /**
   * @param req
   * @param res
   * @param next
   * @return {Promise.<void>}
   */
  update: async (req, res, next) => {
    debug('executing update action');

    let ignoreLabels = req.body;
    if (typeof ignoreLabels === 'string') {
      ignoreLabels = ignoreLabels.split(',');
    }

    try {
      const project = await ProjectService.update(req.params.id, ignoreLabels);
      res.status(200).send(project);
    } catch (err) {
      next(err);
    }
  },
};

module.exports = ReportController;
<commit_msg>Fixed labels saving<commit_after>const debug = require('debug')('github-metrics:controllers:v1:project');

const ProjectService = require('../../services/v1/project');

const ReportController = {

  /**
   * @param req
   * @param res
   * @param next
   * @return {Promise.<void>}
   */
  find: async (req, res, next) => {
    debug('executing find action');

    try {
      const projects = await ProjectService.find();
      res.status(200).send(projects);
    } catch (err) {
      next(err);
    }
  },

  /**
   * @param req
   * @param res
   * @param next
   * @return {Promise.<void>}
   */
  update: async (req, res, next) => {
    debug('executing update action');

    let ignoreLabels = req.body.ignore_labels;
    if (typeof ignoreLabels === 'string') {
      ignoreLabels = ignoreLabels.split(',');
      ignoreLabels = ignoreLabels.map(Number);
    }

    try {
      const project = await ProjectService.update(req.params.id, ignoreLabels);
      res.status(200).send(project);
    } catch (err) {
      next(err);
    }
  },
};

module.exports = ReportController;
<|endoftext|>


<commit_before>/**
 A **CameraControl** pans, rotates and zooms a {{#crossLink "Camera"}}{{/crossLink}} using the mouse and keyboard,
 as well as switches it between preset left, right, anterior, posterior, superior and inferior views.

 A CameraControl contains the following control sub-components, each of which handle an aspect of interaction:

{{#crossLink "KeyboardPanCamera"}}{{/crossLink}} pans the camera with the W,S,A,D,X and Z keys

{{#crossLink "MousePanCamera"}}{{/crossLink}} pans horizontally and vertically by dragging the mouse with left and right buttons down

{{#crossLink "KeyboardRotateCamera"}}{{/crossLink}} rotates the camera with the arrow keys

{{#crossLink "MouseRotateCamera"}}{{/crossLink}} rotates the camera by dragging with the left mouse button down

{{#crossLink "KeyboardZoomCamera"}}{{/crossLink}} zooms the *eye* position closer and further from the *look* position with the + and - keys

{{#crossLink "MouseZoomCamera"}}{{/crossLink}} zooms the *eye* closer and further from *look* using the mousewheel

{{#crossLink "KeyboardAxisCamera"}}{{/crossLink}} between preset left, right, anterior, posterior, superior and inferior views using keys 1-6

{{#crossLink "MousePickEntity"}}{{/crossLink}} TODO

{{#crossLink "CameraFlight"}}{{/crossLink}} TODO

 A CameraControl provides these control sub-components as read-only properties, which allows them to be individually configured (or deactivated) as required.

Activating or deactivating a CameraControl will activate or deactivate all its control sub-components.

Attaching a different {{#crossLink "Camera"}}{{/crossLink}} to the CameraControl will also attach that
 {{#crossLink "Camera"}}{{/crossLink}} to all the control sub-components.

The control sub-components are not supposed to be re-attached to a different {{#crossLink "Camera"}}{{/crossLink}} than the owner CameraControl.

A CameraControl manages the life-cycles of its control sub-components, destroying them when the CameraControl is destroyed.

 ## Example

 ````Javascript
 var camera = new XEO.Camera();

 var cameraControl = new XEO.CameraControl({

        camera: camera,

        // "First person" mode rotates look about eye.
        // By default however, we orbit eye about look.
        firstPerson: false
    });

 // Reduce the sensitivity of mouse rotation
 cameraControl.mouseRotate.sensitivity = 0.7;

 // Deactivate switching between preset views
 cameraControl.keyboardAxis.active = false;

 // Create a Entity
 var entity = new XEO.Entity({
    camera: camera
 });
 ````

 @class CameraControl
 @module XEO
 @submodule controls
 @constructor
 @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
 @param [cfg] {*} Configs
 @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
 @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CameraControl.
 @param [cfg.camera] {String|Camera} ID or instance of a {{#crossLink "Camera"}}Camera{{/crossLink}} to control.
 Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this CameraControl. Defaults to the
 parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/camera:property"}}camera{{/crossLink}}.
 @param [cfg.active=true] {Boolean} Whether or not this CameraControl is active.
 @param [firstPerson=false] {Boolean} Whether or not this CameraControl is in "first person" mode.
 @extends Component
 */
(function () {

    "use strict";

    XEO.CameraControl = XEO.Component.extend({

        /**
         JavaScript class name for this Component.

         @property type
         @type String
         @final
         */
        type: "XEO.CameraControl",

        /**
         Indicates that only one instance of a CameraControl may be active within
         its {{#crossLink "Scene"}}{{/crossLink}} at a time. When a CameraControl is activated, that has
         a true value for this flag, then any other active CameraControl will be deactivated first.

         @property exclusive
         @type Boolean
         @final
         */
        exclusive: true,

        _init: function (cfg) {

            var self = this;

            var scene = this.scene;

            // Shows a bounding box around each Entity we fly to
            this._boundaryEntity = new XEO.Entity(scene, {
                geometry: new XEO.BoundaryGeometry(scene),
                material: new XEO.PhongMaterial(scene, {
                    diffuse: [0, 0, 0],
                    ambient: [0, 0, 0],
                    specular: [0, 0, 0],
                    emissive: [1.0, 1.0, 0.6],
                    lineWidth: 4
                }),
                visibility: new XEO.Visibility(scene, {
                    visible: false
                }),
                modes: new XEO.Modes({

                    // Does not contribute to the size of any enclosing boundaries
                    // that might be calculated by xeoEngine, eg. like that returned by XEO.Scene#worldBoundary
                    collidable: false
                })
            });

            /**
             * The {{#crossLink "KeyboardAxisCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardAxis
             * @final
             * @type KeyboardAxisCamera
             */
            this.keyboardAxis = new XEO.KeyboardAxisCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardRotateCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardOrbit
             * @final
             * @type KeyboardRotateCamera
             */
            this.keyboardRotate = new XEO.KeyboardRotateCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MouseRotateCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mouseRotate
             * @final
             * @type MouseRotateCamera
             */
            this.mouseRotate = new XEO.MouseRotateCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardPanCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardPan
             * @final
             * @type KeyboardPanCamera
             */
            this.keyboardPan = new XEO.KeyboardPanCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MousePanCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mousePan
             * @final
             * @type MousePanCamera
             */
            this.mousePan = new XEO.MousePanCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardZoomCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardZoom
             * @final
             * @type KeyboardZoomCamera
             */
            this.keyboardZoom = new XEO.KeyboardZoomCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MouseZoomCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mouseZoom
             * @final
             * @type MouseZoomCamera
             */
            this.mouseZoom = new XEO.MouseZoomCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MousePickEntity"}}{{/crossLink}} within this CameraControl.
             *
             * @property mousePickEntity
             * @final
             * @type MousePickEntity
             */
            this.mousePickEntity = new XEO.MousePickEntity(scene, {
                rayPick: true
            });

            this.mousePickEntity.on("pick", this._entityPicked, this);

            this.mousePickEntity.on("nopick",
                function (e) {
                    //alert("Nothing picked");
                });

            /**
             * The {{#crossLink "CameraFlight"}}{{/crossLink}} within this CameraControl.
             *
             * @property cameraFlight
             * @final
             * @type CameraFlight
             */
            this.cameraFlight = new XEO.CameraFlight(scene, {
                camera: cfg.camera,
                duration: 0.5
            });

            // Set component properties

            this.firstPerson = cfg.firstPerson;
            this.camera = cfg.camera;
            this.active = cfg.active !== false;
        },

        _entityPicked: function (e) {

            // Fly camera to each picked entity
            // Don't change distance between look and eye

            var view = this.cameraFlight.camera.view;

            var pos;

            if (e.worldPos) {
                pos = e.worldPos

            } else if (e.entity) {
                pos = e.entity.worldBoundary.center
            }

            if (pos) {

                var diff = XEO.math.subVec3(view.eye, view.look, []);

                var input = this.scene.input;

              //  if (input.keyDown[input.KEY_SHIFT] && e.entity) {

                    // var aabb = e.entity.worldBoundary.aabb;

                    this._boundaryEntity.geometry.obb = e.entity.worldBoundary.obb;
                    this._boundaryEntity.visibility.visible = true;

                    var center = e.entity.worldBoundary.center;

                    this.cameraFlight.flyTo({
                            aabb: e.entity.worldBoundary.aabb,
                            oXffset: [
                                pos[0] - center[0],
                                pos[1] - center[1],
                                pos[2] - center[2]
                            ]
                        },
                        this._hideEntityBoundary, this);

                //} else {
                //
                //    this.cameraFlight.flyTo({
                //            look: pos,
                //            eye: [
                //                pos[0] + diff[0],
                //                pos[1] + diff[1],
                //                pos[2] + diff[2]
                //            ]
                //        },
                //        this._hideEntityBoundary, this);
                //}
            }
        },

        _hideEntityBoundary: function () {
            this._boundaryEntity.visibility.visible = false;
        },

        _props: {

            /**
             * Flag which indicates whether this CameraControl is in "first person" mode.
             *
             * In "first person" mode (disabled by default) the look position rotates about the eye position. Otherwise,
             * the eye rotates about the look.
             *
             * Fires a {{#crossLink "KeyboardRotateCamera/firstPerson:event"}}{{/crossLink}} event on change.
             *
             * @property firstPerson
             * @default false
             * @type Boolean
             */
            firstPerson: {

                set: function (value) {

                    value = !!value;

                    this._firstPerson = value;

                    this.keyboardRotate.firstPerson = value;
                    this.mouseRotate.firstPerson = value;

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/firstPerson:property"}}{{/crossLink}} property changes.
                     * @event firstPerson
                     * @param value The property's new value
                     */
                    this.fire('firstPerson', this._firstPerson);
                }

                ,

                get: function () {
                    return this._firstPerson;
                }
            }
            ,

            /**
             * The {{#crossLink "Camera"}}{{/crossLink}} being controlled by this CameraControl.
             *
             * Must be within the same {{#crossLink "Scene"}}{{/crossLink}} as this CameraControl. Defaults to the parent
             * {{#crossLink "Scene"}}Scene's{{/crossLink}} default {{#crossLink "Scene/camera:property"}}camera{{/crossLink}} when set to
             * a null or undefined value.
             *
             * @property camera
             * @type Camera
             */
            camera: {

                set: function (value) {

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/camera:property"}}{{/crossLink}}
                     * property changes.
                     *
                     * @event camera
                     * @param value The property's new value
                     */
                    this._setChild("camera", value);

                    // Update camera on child components

                    var camera = this._children.camera;

                    this.keyboardAxis.camera = camera;
                    this.keyboardRotate.camera = camera;
                    this.mouseRotate.camera = camera;
                    this.keyboardPan.camera = camera;
                    this.mousePan.camera = camera;
                    this.keyboardZoom.camera = camera;
                    this.mouseZoom.camera = camera;
                    this.cameraFlight.camera = camera;
                }

                ,

                get: function () {
                    return this._children.camera;
                }
            }
            ,

            /**
             * Flag which indicates whether this CameraControl is active or not.
             *
             * Fires an {{#crossLink "CameraControl/active:event"}}{{/crossLink}} event on change.
             *
             * @property active
             * @type Boolean
             */
            active: {

                set: function (value) {

                    value = !!value;

                    if (this._active === value) {
                        return;
                    }

                    // Activate or deactivate child components

                    this.keyboardAxis.active = value;
                    this.keyboardRotate.active = value;
                    this.mouseRotate.active = value;
                    this.keyboardPan.active = value;
                    this.mousePan.active = value;
                    this.keyboardZoom.active = value;
                    this.mouseZoom.active = value;
                    this.mousePickEntity.active = value;
                    this.cameraFlight.active = value;

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/active:property"}}{{/crossLink}} property changes.
                     * @event active
                     * @param value The property's new value
                     */
                    this.fire('active', this._active = value);
                },

                get: function () {
                    return this._active;
                }
            }
        }
        ,

        _getJSON: function () {

            var json = {
                firstPerson: this._firstPerson,
                active: this._active
            };

            if (this._children.camera) {
                json.camera = this._children.camera.id;
            }

            return json;
        }
        ,

        _destroy: function () {

            this.active = false;

            // FIXME: Does not recursively destroy child components
            this._boundaryEntity.destroy();

            this.keyboardAxis.destroy();
            this.keyboardRotate.destroy();
            this.mouseRotate.destroy();
            this.keyboardPan.destroy();
            this.mousePan.destroy();
            this.keyboardZoom.destroy();
            this.mouseZoom.destroy();
            this.mousePickEntity.destroy();
            this.cameraFlight.destroy();
        }
    });

})();
<commit_msg>Fix scene mismatch in XEO.CameraControl<commit_after>/**
 A **CameraControl** pans, rotates and zooms a {{#crossLink "Camera"}}{{/crossLink}} using the mouse and keyboard,
 as well as switches it between preset left, right, anterior, posterior, superior and inferior views.

 A CameraControl contains the following control sub-components, each of which handle an aspect of interaction:

{{#crossLink "KeyboardPanCamera"}}{{/crossLink}} pans the camera with the W,S,A,D,X and Z keys

{{#crossLink "MousePanCamera"}}{{/crossLink}} pans horizontally and vertically by dragging the mouse with left and right buttons down

{{#crossLink "KeyboardRotateCamera"}}{{/crossLink}} rotates the camera with the arrow keys

{{#crossLink "MouseRotateCamera"}}{{/crossLink}} rotates the camera by dragging with the left mouse button down

{{#crossLink "KeyboardZoomCamera"}}{{/crossLink}} zooms the *eye* position closer and further from the *look* position with the + and - keys

{{#crossLink "MouseZoomCamera"}}{{/crossLink}} zooms the *eye* closer and further from *look* using the mousewheel

{{#crossLink "KeyboardAxisCamera"}}{{/crossLink}} between preset left, right, anterior, posterior, superior and inferior views using keys 1-6

{{#crossLink "MousePickEntity"}}{{/crossLink}} TODO

{{#crossLink "CameraFlight"}}{{/crossLink}} TODO

 A CameraControl provides these control sub-components as read-only properties, which allows them to be individually configured (or deactivated) as required.

Activating or deactivating a CameraControl will activate or deactivate all its control sub-components.

Attaching a different {{#crossLink "Camera"}}{{/crossLink}} to the CameraControl will also attach that
 {{#crossLink "Camera"}}{{/crossLink}} to all the control sub-components.

The control sub-components are not supposed to be re-attached to a different {{#crossLink "Camera"}}{{/crossLink}} than the owner CameraControl.

A CameraControl manages the life-cycles of its control sub-components, destroying them when the CameraControl is destroyed.

 ## Example

 ````Javascript
 var camera = new XEO.Camera();

 var cameraControl = new XEO.CameraControl({

        camera: camera,

        // "First person" mode rotates look about eye.
        // By default however, we orbit eye about look.
        firstPerson: false
    });

 // Reduce the sensitivity of mouse rotation
 cameraControl.mouseRotate.sensitivity = 0.7;

 // Deactivate switching between preset views
 cameraControl.keyboardAxis.active = false;

 // Create a Entity
 var entity = new XEO.Entity({
    camera: camera
 });
 ````

 @class CameraControl
 @module XEO
 @submodule controls
 @constructor
 @param [scene] {Scene} Parent {{#crossLink "Scene"}}{{/crossLink}}.
 @param [cfg] {*} Configs
 @param [cfg.id] {String} Optional ID, unique among all components in the parent scene, generated automatically when omitted.
 @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this CameraControl.
 @param [cfg.camera] {String|Camera} ID or instance of a {{#crossLink "Camera"}}Camera{{/crossLink}} to control.
 Must be within the same {{#crossLink "Scene"}}Scene{{/crossLink}} as this CameraControl. Defaults to the
 parent {{#crossLink "Scene"}}Scene{{/crossLink}}'s default instance, {{#crossLink "Scene/camera:property"}}camera{{/crossLink}}.
 @param [cfg.active=true] {Boolean} Whether or not this CameraControl is active.
 @param [firstPerson=false] {Boolean} Whether or not this CameraControl is in "first person" mode.
 @extends Component
 */
(function () {

    "use strict";

    XEO.CameraControl = XEO.Component.extend({

        /**
         JavaScript class name for this Component.

         @property type
         @type String
         @final
         */
        type: "XEO.CameraControl",

        /**
         Indicates that only one instance of a CameraControl may be active within
         its {{#crossLink "Scene"}}{{/crossLink}} at a time. When a CameraControl is activated, that has
         a true value for this flag, then any other active CameraControl will be deactivated first.

         @property exclusive
         @type Boolean
         @final
         */
        exclusive: true,

        _init: function (cfg) {

            var self = this;

            var scene = this.scene;

            // Shows a bounding box around each Entity we fly to
            this._boundaryEntity = new XEO.Entity(scene, {
                geometry: new XEO.BoundaryGeometry(scene),
                material: new XEO.PhongMaterial(scene, {
                    diffuse: [0, 0, 0],
                    ambient: [0, 0, 0],
                    specular: [0, 0, 0],
                    emissive: [1.0, 1.0, 0.6],
                    lineWidth: 4
                }),
                visibility: new XEO.Visibility(scene, {
                    visible: false
                }),
                modes: new XEO.Modes(scene, {

                    // Does not contribute to the size of any enclosing boundaries
                    // that might be calculated by xeoEngine, eg. like that returned by XEO.Scene#worldBoundary
                    collidable: false
                })
            });

            /**
             * The {{#crossLink "KeyboardAxisCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardAxis
             * @final
             * @type KeyboardAxisCamera
             */
            this.keyboardAxis = new XEO.KeyboardAxisCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardRotateCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardOrbit
             * @final
             * @type KeyboardRotateCamera
             */
            this.keyboardRotate = new XEO.KeyboardRotateCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MouseRotateCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mouseRotate
             * @final
             * @type MouseRotateCamera
             */
            this.mouseRotate = new XEO.MouseRotateCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardPanCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardPan
             * @final
             * @type KeyboardPanCamera
             */
            this.keyboardPan = new XEO.KeyboardPanCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MousePanCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mousePan
             * @final
             * @type MousePanCamera
             */
            this.mousePan = new XEO.MousePanCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "KeyboardZoomCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property keyboardZoom
             * @final
             * @type KeyboardZoomCamera
             */
            this.keyboardZoom = new XEO.KeyboardZoomCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MouseZoomCamera"}}{{/crossLink}} within this CameraControl.
             *
             * @property mouseZoom
             * @final
             * @type MouseZoomCamera
             */
            this.mouseZoom = new XEO.MouseZoomCamera(scene, {
                camera: cfg.camera
            });

            /**
             * The {{#crossLink "MousePickEntity"}}{{/crossLink}} within this CameraControl.
             *
             * @property mousePickEntity
             * @final
             * @type MousePickEntity
             */
            this.mousePickEntity = new XEO.MousePickEntity(scene, {
                rayPick: true
            });

            this.mousePickEntity.on("pick", this._entityPicked, this);

            this.mousePickEntity.on("nopick",
                function (e) {
                    //alert("Nothing picked");
                });

            /**
             * The {{#crossLink "CameraFlight"}}{{/crossLink}} within this CameraControl.
             *
             * @property cameraFlight
             * @final
             * @type CameraFlight
             */
            this.cameraFlight = new XEO.CameraFlight(scene, {
                camera: cfg.camera,
                duration: 0.5
            });

            // Set component properties

            this.firstPerson = cfg.firstPerson;
            this.camera = cfg.camera;
            this.active = cfg.active !== false;
        },

        _entityPicked: function (e) {

            // Fly camera to each picked entity
            // Don't change distance between look and eye

            var view = this.cameraFlight.camera.view;

            var pos;

            if (e.worldPos) {
                pos = e.worldPos

            } else if (e.entity) {
                pos = e.entity.worldBoundary.center
            }

            if (pos) {

                var diff = XEO.math.subVec3(view.eye, view.look, []);

                var input = this.scene.input;

              //  if (input.keyDown[input.KEY_SHIFT] && e.entity) {

                    // var aabb = e.entity.worldBoundary.aabb;

                    this._boundaryEntity.geometry.obb = e.entity.worldBoundary.obb;
                    this._boundaryEntity.visibility.visible = true;

                    var center = e.entity.worldBoundary.center;

                    this.cameraFlight.flyTo({
                            aabb: e.entity.worldBoundary.aabb,
                            oXffset: [
                                pos[0] - center[0],
                                pos[1] - center[1],
                                pos[2] - center[2]
                            ]
                        },
                        this._hideEntityBoundary, this);

                //} else {
                //
                //    this.cameraFlight.flyTo({
                //            look: pos,
                //            eye: [
                //                pos[0] + diff[0],
                //                pos[1] + diff[1],
                //                pos[2] + diff[2]
                //            ]
                //        },
                //        this._hideEntityBoundary, this);
                //}
            }
        },

        _hideEntityBoundary: function () {
            this._boundaryEntity.visibility.visible = false;
        },

        _props: {

            /**
             * Flag which indicates whether this CameraControl is in "first person" mode.
             *
             * In "first person" mode (disabled by default) the look position rotates about the eye position. Otherwise,
             * the eye rotates about the look.
             *
             * Fires a {{#crossLink "KeyboardRotateCamera/firstPerson:event"}}{{/crossLink}} event on change.
             *
             * @property firstPerson
             * @default false
             * @type Boolean
             */
            firstPerson: {

                set: function (value) {

                    value = !!value;

                    this._firstPerson = value;

                    this.keyboardRotate.firstPerson = value;
                    this.mouseRotate.firstPerson = value;

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/firstPerson:property"}}{{/crossLink}} property changes.
                     * @event firstPerson
                     * @param value The property's new value
                     */
                    this.fire('firstPerson', this._firstPerson);
                }

                ,

                get: function () {
                    return this._firstPerson;
                }
            }
            ,

            /**
             * The {{#crossLink "Camera"}}{{/crossLink}} being controlled by this CameraControl.
             *
             * Must be within the same {{#crossLink "Scene"}}{{/crossLink}} as this CameraControl. Defaults to the parent
             * {{#crossLink "Scene"}}Scene's{{/crossLink}} default {{#crossLink "Scene/camera:property"}}camera{{/crossLink}} when set to
             * a null or undefined value.
             *
             * @property camera
             * @type Camera
             */
            camera: {

                set: function (value) {

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/camera:property"}}{{/crossLink}}
                     * property changes.
                     *
                     * @event camera
                     * @param value The property's new value
                     */
                    this._setChild("camera", value);

                    // Update camera on child components

                    var camera = this._children.camera;

                    this.keyboardAxis.camera = camera;
                    this.keyboardRotate.camera = camera;
                    this.mouseRotate.camera = camera;
                    this.keyboardPan.camera = camera;
                    this.mousePan.camera = camera;
                    this.keyboardZoom.camera = camera;
                    this.mouseZoom.camera = camera;
                    this.cameraFlight.camera = camera;
                }

                ,

                get: function () {
                    return this._children.camera;
                }
            }
            ,

            /**
             * Flag which indicates whether this CameraControl is active or not.
             *
             * Fires an {{#crossLink "CameraControl/active:event"}}{{/crossLink}} event on change.
             *
             * @property active
             * @type Boolean
             */
            active: {

                set: function (value) {

                    value = !!value;

                    if (this._active === value) {
                        return;
                    }

                    // Activate or deactivate child components

                    this.keyboardAxis.active = value;
                    this.keyboardRotate.active = value;
                    this.mouseRotate.active = value;
                    this.keyboardPan.active = value;
                    this.mousePan.active = value;
                    this.keyboardZoom.active = value;
                    this.mouseZoom.active = value;
                    this.mousePickEntity.active = value;
                    this.cameraFlight.active = value;

                    /**
                     * Fired whenever this CameraControl's {{#crossLink "CameraControl/active:property"}}{{/crossLink}} property changes.
                     * @event active
                     * @param value The property's new value
                     */
                    this.fire('active', this._active = value);
                },

                get: function () {
                    return this._active;
                }
            }
        }
        ,

        _getJSON: function () {

            var json = {
                firstPerson: this._firstPerson,
                active: this._active
            };

            if (this._children.camera) {
                json.camera = this._children.camera.id;
            }

            return json;
        }
        ,

        _destroy: function () {

            this.active = false;

            // FIXME: Does not recursively destroy child components
            this._boundaryEntity.destroy();

            this.keyboardAxis.destroy();
            this.keyboardRotate.destroy();
            this.mouseRotate.destroy();
            this.keyboardPan.destroy();
            this.mousePan.destroy();
            this.keyboardZoom.destroy();
            this.mouseZoom.destroy();
            this.mousePickEntity.destroy();
            this.cameraFlight.destroy();
        }
    });

})();
<|endoftext|>


<commit_before><commit_msg>Restored `Delete Todo` option<commit_after><|endoftext|>


<commit_before><commit_msg>Introduce explicit Capture method. 'Capture.attach'<commit_after><|endoftext|>


<commit_before>/* ========================================================================
 * DBH-PG: test
 * ========================================================================
 * Copyright 2014 Sapienlab
 * Licensed under MIT (https://github.com/sapienlab/dbh-pg/blob/master/LICENSE)
 * ======================================================================== */

describe('DBH', function() {
    'use strict';

    var assert = require('assert'),
        DBH = require('../'),
        Promise = require('bluebird'),
        using = Promise.using,
        db = new DBH('postgres://postgres@localhost/db2test'),
        people = [
            {name: 'Aaron',    age: 10},
            {name: 'Brian',    age: 20},
            {name: 'Chris',    age: 30},
            {name: 'David',    age: 40},
            {name: 'Elvis',    age: 50},
            {name: 'Frank',    age: 60},
            {name: 'Grace',    age: 70},
            {name: 'Haley',    age: 80},
            {name: 'Irma',     age: 90},
            {name: 'Jenny',    age: 100},
            {name: 'Kevin',    age: 110},
            {name: 'Larry',    age: 120},
            {name: 'Michelle', age: 130},
            {name: 'Nancy',    age: 140},
            {name: 'Olivia',   age: 150},
            {name: 'Peter',    age: 160},
            {name: 'Quinn',    age: 170},
            {name: 'Ronda',    age: 180},
            {name: 'Shelley',  age: 190},
            {name: 'Tobias',   age: 200},
            {name: 'Uma',      age: 210},
            {name: 'Veena',    age: 220},
            {name: 'Wanda',    age: 230},
            {name: 'Xavier',   age: 240},
            {name: 'Yoyo',     age: 250},
            {name: 'Zanzabar', age: 260}
        ];

    before(function() {

        Promise.longStackTraces();

        db.verbose = true;

        return using(db.conn(), function (conn) {
            return conn.exec(
                'create table person(\
                    id serial primary key,\
                    name varchar(10),\
                    age integer\
                )'
            ).then(function () {
                var me = this,
                prepared = DBH.prepare('insert into person(name, age) values ($1, $2)');
                return Promise.all(people.map(function (person) {
                    return me.exec(prepared([person.name, person.age]));
                }));
            });
        });

    });

    describe('elemental', function() {

        it('db is instance of DBH', function() {
            assert.equal(db instanceof DBH, true);
        });

        it('DBH.using is strict equals Promise.using', function() {
            assert.equal(DBH.using === Promise.using, true);
        });

    });

    describe('#conn', function() {

        it('create connection', function() {
            return using(db.conn(), function (conn) {
                return true;
            });
        });

        it('exec "select count(age) from person" whith conn.exec', function() {
            return using(db.conn(), function (conn) {
                return conn.exec('select count(age) from person')
                    .then(function (result) {
                        assert.equal(result.rows[0].count, 26);
                    });
            });
        });

        it('exec "select count(age) from person" whith conn.count', function() {
            return using(db.conn(), function (conn) {
                return conn.count('person')
                    .then(function (count) {
                        assert.equal(count, 26);
                    });
            });
        });

        it('exec "select count(age) from person" whith conn.fetchScalar', function() {
            return using(db.conn(), function (conn) {
                return conn.fetchScalar('select count(age) from person')
                    .then(function (count) {
                        assert.equal(count, 26);
                    });
            });
        });

    });

    describe('transactions', function () {

        it('without commit', function() {
            var id = 2,
                oldName = 'Old Name',
                newName = 'New Name',
                query = 'select name from person where id=' + id;
            return using(db.conn(), function (conn) {
                return conn.update('person', { name: oldName }, { id : id })
                    .then(DBH.fetchScalar(query))
                    .then(function (name) {
                        assert.equal(name, oldName);
                    })
                    .then(DBH.begin()) // start transaction
                    .then(DBH.update('person', { name: newName }, { id: id }));
                // because not commit is here, then auto rollback must be called
            }).then(function () {
                return using(db.conn(), function (conn) {
                    return conn.fetchScalar(query)
                        .then(function (name) {
                            assert.equal(oldName, name);
                            assert.notEqual(name, newName);
                        });
                });
            });
        });

        it('with commit', function() {
            var id = 3,
                oldName = 'Old Name',
                newName = 'New Name',
                query = 'select name from person where id=' + id;
            return using(db.conn(), function (conn) {
                return conn.update('person', { name: oldName }, { id : id })
                    .then(DBH.fetchScalar(query))
                    .then(function (name) {
                        assert.equal(name, oldName);
                    })
                    .then(DBH.begin()) // start transaction
                    .then(DBH.update('person', { name: newName }, { id: id }))
                    .then(DBH.commit());
            }).then(function () {
                return using(db.conn(), function (conn) {
                    return conn.fetchScalar(query)
                        .then(function (name) {
                            assert.notEqual(oldName, name);
                            assert.equal(name, newName);
                        });
                });
            });
        });

    });

    describe('fetchOne', function () {

        var person1 = people[0];
        person1.id = 1;

        it('simple', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=1');
            }).then(function(person) {
                assert.deepEqual(person, person1);
            });
        });

        it('named param', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=$id', { id: 1 });
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });

        it('numeric param', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=$1', [1]);
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });

        it('first', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person');
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });
        it('not exists', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=99999');
            }).then(function(person) {
                assert.deepEqual(person, null)
            });
        });

    });

});
<commit_msg>fix error<commit_after>/* ========================================================================
 * DBH-PG: test
 * ========================================================================
 * Copyright 2014 Sapienlab
 * Licensed under MIT (https://github.com/sapienlab/dbh-pg/blob/master/LICENSE)
 * ======================================================================== */

describe('DBH', function() {
    'use strict';

    var assert = require('assert'),
        DBH = require('../'),
        Promise = require('bluebird'),
        using = Promise.using,
        db = new DBH('postgres://postgres@localhost/db2test'),
        people = [
            {name: 'Aaron',    age: 10},
            {name: 'Brian',    age: 20},
            {name: 'Chris',    age: 30},
            {name: 'David',    age: 40},
            {name: 'Elvis',    age: 50},
            {name: 'Frank',    age: 60},
            {name: 'Grace',    age: 70},
            {name: 'Haley',    age: 80},
            {name: 'Irma',     age: 90},
            {name: 'Jenny',    age: 100},
            {name: 'Kevin',    age: 110},
            {name: 'Larry',    age: 120},
            {name: 'Michelle', age: 130},
            {name: 'Nancy',    age: 140},
            {name: 'Olivia',   age: 150},
            {name: 'Peter',    age: 160},
            {name: 'Quinn',    age: 170},
            {name: 'Ronda',    age: 180},
            {name: 'Shelley',  age: 190},
            {name: 'Tobias',   age: 200},
            {name: 'Uma',      age: 210},
            {name: 'Veena',    age: 220},
            {name: 'Wanda',    age: 230},
            {name: 'Xavier',   age: 240},
            {name: 'Yoyo',     age: 250},
            {name: 'Zanzabar', age: 260}
        ];

    before(function() {

        Promise.longStackTraces();

        db.verbose = true;

        return using(db.conn(), function (conn) {
            return conn.exec(
                'create table person(\
                    id serial primary key,\
                    name varchar(10),\
                    age integer\
                )'
            ).then(function () {
                var me = this,
                prepared = DBH.prepare('insert into person(name, age) values ($1, $2)');
                return Promise.all(people.map(function (person) {
                    return me.exec(prepared([person.name, person.age]));
                }));
            });
        });

    });

    describe('elemental', function() {

        it('db is instance of DBH', function() {
            assert.equal(db instanceof DBH, true);
        });

        it('DBH.using is strict equals Promise.using', function() {
            assert.equal(DBH.using === Promise.using, true);
        });

    });

    describe('#conn', function() {

        it('create connection', function() {
            return using(db.conn(), function (conn) {
                return true;
            });
        });

        it('exec "select count(age) from person" whith conn.exec', function() {
            return using(db.conn(), function (conn) {
                return conn.exec('select count(age) from person')
                    .then(function (result) {
                        assert.equal(result.rows[0].count, 26);
                    });
            });
        });

        it('exec "select count(age) from person" whith conn.count', function() {
            return using(db.conn(), function (conn) {
                return conn.count('person')
                    .then(function (count) {
                        assert.equal(count, 26);
                    });
            });
        });

        it('exec "select count(age) from person" whith conn.fetchScalar', function() {
            return using(db.conn(), function (conn) {
                return conn.fetchScalar('select count(age) from person')
                    .then(function (count) {
                        assert.equal(count, 26);
                    });
            });
        });

    });

    describe('transactions', function () {

        it('without commit', function() {
            var id = 2,
                oldName = 'Old Name',
                newName = 'New Name',
                query = 'select name from person where id=' + id;
            return using(db.conn(), function (conn) {
                return conn.update('person', { name: oldName }, { id : id })
                    .then(DBH.fetchScalar(query))
                    .then(function (name) {
                        assert.equal(name, oldName);
                    })
                    .then(DBH.begin()) // start transaction
                    .then(DBH.update('person', { name: newName }, { id: id }));
                // because not commit is here, then auto rollback must be called
            }).then(function () {
                return using(db.conn(), function (conn) {
                    return conn.fetchScalar(query)
                        .then(function (name) {
                            assert.equal(oldName, name);
                            assert.notEqual(name, newName);
                        });
                });
            });
        });

        it('with commit', function() {
            var id = 3,
                oldName = 'Old Name',
                newName = 'New Name',
                query = 'select name from person where id=' + id;
            return using(db.conn(), function (conn) {
                return conn.update('person', { name: oldName }, { id : id })
                    .then(DBH.fetchScalar(query))
                    .then(function (name) {
                        assert.equal(name, oldName);
                    })
                    .then(DBH.begin()) // start transaction
                    .then(DBH.update('person', { name: newName }, { id: id }))
                    .then(DBH.commit());
            }).then(function () {
                return using(db.conn(), function (conn) {
                    return conn.fetchScalar(query)
                        .then(function (name) {
                            assert.notEqual(oldName, name);
                            assert.equal(name, newName);
                        });
                });
            });
        });

    });

    describe('fetchOne', function () {

        var person1 = people[0];
        person1.id = 1;

        it('simple', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=1');
            }).then(function(person) {
                assert.deepEqual(person, person1);
            });
        });

        it('named param', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=$id', { id: 1 });
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });

        it('numeric param', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=$1', [1]);
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });

        it('first', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person');
            }).then(function(person) {
                assert.deepEqual(person, person1)
            });
        });
        it('not exists', function() {
            return using(db.conn(), function(conn) {
                return conn.fetchOne('select id, name, age from person where id=99999');
            }).then(function(person) {
                assert.strictEqual(person, undefined)
            });
        });

    });

});
<|endoftext|>


<commit_before>// Converts integer to roman numeral
function romanize(num) {
    var lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1},
	roman = '',
	    i;
	for ( i in lookup ) {
	    while ( num >= lookup[i] ) {
		roman += i;
		num -= lookup[i];
	    }
	}
	return roman;
 }

// Builds a
 Table of Contents from all <headers> in DOM
function createTOC(){
    var toc = "";
    var level = 0;
    var levels = {}
    $('#toc').html('');

    $(":header").each(function(i){
	    if (this.id=='tocheading'){return;}

	    titleText = this.innerHTML;
	    openLevel = this.tagName[1];

	    if (levels[openLevel]){
		levels[openLevel] += 1;
	    } else{
		levels[openLevel] = 1;
	    }

	    if (openLevel > level) {
		toc += (new Array(openLevel - level + 1)).join('
');
	    } else if (openLevel < level) {
		toc += (new Array(level - openLevel + 1)).join("
");
		for (i=level;i>openLevel;i--){levels[i]=0;}
	    }

	    level = parseInt(openLevel);

	    if (this.id==''){this.id = this.innerHTML.replace(/ /g,"-")}
	    var anchor = this.id;

	    toc += '
' +  (levels[openLevel].toString()) + '. §§§§§§§§§§§§§§ ' + titleText
		+ '
';

	});

    if (level) {
	toc += (new Array(level + 1)).join("
");
    }

    $('#toc').append(toc);

};

// Executes the createToc function
setTimeout(function(){createTOC();},100);

// Rebuild to TOC every minute
setInterval(function(){createTOC();},60000);
<commit_msg>added ipython_notebook_toc.js<commit_after>// Converts integer to roman numeral
function romanize(num) {
    var lookup = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1},
	roman = '',
	    i;
	for ( i in lookup ) {
	    while ( num >= lookup[i] ) {
		roman += i;
		num -= lookup[i];
	    }
	}
	return roman;
 }

// Builds a
 Table of Contents from all <headers> in DOM
function createTOC(){
    var toc = "";
    var level = 0;
    var levels = {}
    $('#toc').html('');

    $(":header").each(function(i){
	    if (this.id=='tocheading'){return;}

	    titleText = this.innerHTML;
	    openLevel = this.tagName[1];

	    if (levels[openLevel]){
		levels[openLevel] += 1;
	    } else{
		levels[openLevel] = 1;
	    }

	    if (openLevel > level) {
		toc += (new Array(openLevel - level + 1)).join('
');
	    } else if (openLevel < level) {
		toc += (new Array(level - openLevel + 1)).join("
");
		for (i=level;i>openLevel;i--){levels[i]=0;}
	    }

	    level = parseInt(openLevel);

	    if (this.id==''){this.id = this.innerHTML.replace(/ /g,"-")}
	    var anchor = this.id;

	    //toc += '
' +  (levels[openLevel].toString()) + '. ' + titleText
		//+ '
';

	});

    if (level) {
	toc += (new Array(level + 1)).join("
");
    }

    $('#toc').append(toc);

};

// Executes the createToc function
setTimeout(function(){createTOC();},100);

// Rebuild to TOC every minute
setInterval(function(){createTOC();},60000);
<|endoftext|>