<commit_before>var ddg_spice_airlines = function(api_result) {
    "use strict";

    var STATUS = {
        'S': 'Scheduled',
        'A': 'In the air',
        'U': 'Unknown status',
        'R': 'Redirected flight',
        'L': 'Landed',
        'D': 'Diverted',
        'C': 'Cancelled',
        'NO': 'Not Operational',
        'DN': 'Data Needed'
    };

    var status = function(flight) {
        return STATUS[flight.StatusCode] + ": Flight Status for " + flight.Airline.Name;
    }

    // Display the plug-in.
    Spice.render({
        data             : api_result,
        header1          : status(api_result.flight),
        source_url       : "http://www.flightstats.com",
        source_name      : "FlightStats",
        template_normal  : "airlines",
        force_big_header : true
    });
};

(function() {
    "use strict";

    var MILLIS_PER_MIN = 60000;
    var MILLIS_PER_HOUR = MILLIS_PER_MIN * 60;

    // Parse string, and return the date (either arrival or departure date).
    var getDateFromString = function(date) {
        date = date.match(/([\d]{4})\-([\d]{2})\-([\d]{2})T([\d]{2}):([\d]{2}):([\d]{2})/);
        var now = new Date();
        var milliseconds = Date.UTC(date[1], date[2] - 1, date[3], date[4], date[5], date[6]) +
                                (now.getTimezoneOffset() * MILLIS_PER_MIN);
        return new Date(milliseconds);
    };

    // Compute the difference between now and the time of departure or arrival.
    var relativeTime = function(date, airportOffset) {
        // This is the time of departure or arrival (not sure why we're getting the difference).
        date = date.getTime() - (date.getTimezoneOffset() * MILLIS_PER_MIN);

        // This is the current time at the airport (in milliseconds).
        var now = new Date().getTime() + (airportOffset * MILLIS_PER_HOUR);

        return date - now;
    };

    // Convert the time from milliseconds to hours and minutes.
    var toTime = function(delta) {
        var time = "";
        var hours = Math.abs(Math.floor(delta / MILLIS_PER_HOUR));
        var minutes = Math.abs(Math.floor((delta % MILLIS_PER_HOUR) / MILLIS_PER_MIN));
        if (0 < hours) {
            time += hours + " hrs ";
        }
        if (0 < minutes) {
            time += minutes + " mins ";
        }
        return time;
    };

    // Check when the plane will depart (or if it has departed).
    Handlebars.registerHelper("status", function(actualDate, publishedDate, airportOffset, isDeparture) {
        var dateString = actualDate || publishedDate
        var dateObject = getDateFromString(dateString);

        var delta = relativeTime(dateObject, airportOffset);

        if(isDeparture) {
            if(delta === 0) {
                return "Departing";
            } else if(delta > 0) {
                return "Departs";
            } else {
                return "Departed";
            }
        } else {
            if(delta === 0) {
                return "Arriving";
            } else if(delta > 0) {
                return "Arrives";
            } else {
                return "Arrived";
            }
        }
    });

    Handlebars.registerHelper("relative", function(actualDate, publishedDate, airportOffset) {
        var dateString = actualDate || publishedDate
        var dateObject = getDateFromString(dateString);

        var delta = relativeTime(dateObject, airportOffset);
        var time = toTime(delta);

        return time;
    });

    Handlebars.registerHelper("airportName", function(name) {
        return name.replace(/airport|international/ig, "");
    });

    Handlebars.registerHelper("checkGate", function(gate) {
        return gate || " — ";
    });

    Handlebars.registerHelper("time", function(actualDate, publishedDate) {
        var dateString = actualDate || publishedDate
        var dateObject = getDateFromString(dateString);
        console.log(dateObject);

        var hours = dateObject.getHours();
        var minutes = dateObject.getMinutes();

        var date = dateObject.toDateString();
        date = date.substring(0, date.lastIndexOf(" "));

        var suffix = "";
        // AM or PM?
        if(hours >= 12) {
            suffix = "p.m.";
        } else {
            suffix = "a.m.";
        }

        // Convert to 12-hour clock.
        if(hours > 12) {
            hours -= 12;
        }

        // Add leading zeroes.
        minutes = minutes < 10 ? "0" + minutes : minutes;

        return date + ", " + hours + ":" + minutes + " " + suffix;
    });
}());<commit_msg>Flights: Added some error checking.<commit_after>var ddg_spice_airlines = function(api_result) {
    "use strict";

    // Check if we have anything returned.
    if(!api_result || !api_result.flight) {
        return;
    }

    var STATUS = {
        'S': 'Scheduled',
        'A': 'In the air',
        'U': 'Unknown status',
        'R': 'Redirected flight',
        'L': 'Landed',
        'D': 'Diverted',
        'C': 'Cancelled',
        'NO': 'Not Operational',
        'DN': 'Data Needed'
    };

    // Make the header.
    var status = function(flight) {
        return STATUS[flight.StatusCode] + ": Flight Status for " + flight.Airline.Name;
    };

    // Display the plug-in.
    Spice.render({
        data             : api_result,
        header1          : status(api_result.flight),
        source_url       : "http://www.flightstats.com",
        source_name      : "FlightStats",
        template_normal  : "airlines",
        force_big_header : true
    });
};

(function() {
    "use strict";

    var MILLIS_PER_MIN = 60000;
    var MILLIS_PER_HOUR = MILLIS_PER_MIN * 60;

    // Parse string, and return the date (either arrival or departure date).
    var getDateFromString = function(date) {
        date = date.match(/([\d]{4})\-([\d]{2})\-([\d]{2})T([\d]{2}):([\d]{2}):([\d]{2})/);
        var now = new Date();
        var milliseconds = Date.UTC(date[1], date[2] - 1, date[3], date[4], date[5], date[6]) +
                                (now.getTimezoneOffset() * MILLIS_PER_MIN);
        return new Date(milliseconds);
    };

    // Compute the difference between now and the time of departure or arrival.
    var relativeTime = function(date, airportOffset) {
        // This is the time of departure or arrival (not sure why we're getting the difference).
        date = date.getTime() - (date.getTimezoneOffset() * MILLIS_PER_MIN);

        // This is the current time at the airport (in milliseconds).
        var now = new Date().getTime() + (airportOffset * MILLIS_PER_HOUR);

        return date - now;
    };

    // Convert the time from milliseconds to hours and minutes.
    var toTime = function(delta) {
        var time = "";
        var hours = Math.abs(Math.floor(delta / MILLIS_PER_HOUR));
        var minutes = Math.abs(Math.floor((delta % MILLIS_PER_HOUR) / MILLIS_PER_MIN));
        if (0 < hours) {
            time += hours + " hrs ";
        }
        if (0 < minutes) {
            time += minutes + " mins ";
        }
        return time;
    };

    // Check when the plane will depart (or if it has departed).
    Handlebars.registerHelper("status", function(actualDate, publishedDate, airportOffset, isDeparture) {
        var dateString = actualDate || publishedDate;
        var dateObject = getDateFromString(dateString);

        var delta = relativeTime(dateObject, airportOffset);

        if(isDeparture) {
            if(delta === 0) {
                return "Departing";
            } else if(delta > 0) {
                return "Departs";
            } else {
                return "Departed";
            }
        } else {
            if(delta === 0) {
                return "Arriving";
            } else if(delta > 0) {
                return "Arrives";
            } else {
                return "Arrived";
            }
        }
    });

    Handlebars.registerHelper("relative", function(actualDate, publishedDate, airportOffset) {
        var dateString = actualDate || publishedDate;
        var dateObject = getDateFromString(dateString);

        var delta = relativeTime(dateObject, airportOffset);
        var time = toTime(delta);

        return time;
    });

    Handlebars.registerHelper("airportName", function(name) {
        return name.replace(/airport|international/ig, "");
    });

    Handlebars.registerHelper("checkGate", function(gate) {
        return gate || " — ";
    });

    Handlebars.registerHelper("time", function(actualDate, publishedDate) {
        var dateString = actualDate || publishedDate;
        var dateObject = getDateFromString(dateString);

        var hours = dateObject.getHours();
        var minutes = dateObject.getMinutes();

        var date = dateObject.toDateString();
        date = date.substring(0, date.lastIndexOf(" "));

        var suffix = "";
        // AM or PM?
        if(hours >= 12) {
            suffix = "p.m.";
        } else {
            suffix = "a.m.";
        }

        // Convert to 12-hour clock.
        if(hours > 12) {
            hours -= 12;
        }

        // Add leading zeroes.
        minutes = minutes < 10 ? "0" + minutes : minutes;

        return date + ", " + hours + ":" + minutes + " " + suffix;
    });
}());<|endoftext|>


<commit_before>function ddg_spice_news(api_result) {
    "use strict";

    // Words that we have to skip in DDG.isRelevant.
    var skip = [
        "news",
        "headline",
        "headlines",
        "latest",
        "breaking",
        "update",
        "s:d",
        "sort:date"
    ];

    // Check if the word news is in the query.
    var generic = false;
    if((/\bnews\b/i).test(DDG.get_query())) {
        generic = true;
    }

    // Some sources need to be set by us.
    var getSource = function(story) {
        switch(story.syndicate) {
            case "Topsy":
            story.source = story.author || "Topsy";
            break;
            case "NewsCred":
            if(story.source) {
		if(story.author) {
		    story.source = story.source + " by " + story.author;
		}
	    } else {
		story.source = "NewsCred";
	    }
            break;
        }
    };

    var ellipsis = function(text, limit) {
	var result = [];
	var count = 0;
	var words = text.split(" ");

	for(var i = 0; i < words.length; i++) {
	    count += words[i].length + 1;
	    if(count < limit) {
		result.push(words[i]);
	    }
	}

	if(words.length > result.length &&
	   !(result[result.length - 1].match(/\.$/))) {
	    result.push("...");
	}

	return result.join(" ");
    };

    var story;
    for(var i = 0, story; story = api_result[i]; i++) {
	story.title = story.title.replace(/<b>|<\/b>|:/g, "");
    }

    var good_stories = [];
    if(generic) {
        good_stories = api_result;
    } else {
        for(var i = 0, story; story = api_result[i]; i++) {
	    if(DDG.isRelevant(story.title, skip, 3)) {
                getSource(story);
                good_stories.push(story);
            }
        }
    }

    // Exit if we didn't get any results.
    if(good_stories.length === 0) {
        return;
    }

    if(good_stories.length > 1) {
	for(var i = 0, good_stories; story = api_result[i]; i++) {
	    story.title = ellipsis(story.title, 55);
	    story.excerpt = ellipsis(story.excerpt, 130);
	}
    }

    // Limit the number of stories to 5.
    // good_stories.splice(5);

    // Display the plugin.
    Spice.render({
        header1: good_stories[0].query +  " (News)",
	source_url: good_stories[0].url,
	source_name: good_stories[0].source,

	spice_name: "news",

	template_frame: "carousel",
	template_options: {
	    items: good_stories,
	    template_item: "news",
	    li_width: 196,
	    li_height: 155
	},

	force_big_header: true,
	force_no_fold: true
    });
}

Handlebars.registerHelper("getIcon", function(url) {
    return Handlebars.helpers.favicon.call({source_url: url, forces: {}});
});

Handlebars.registerHelper("getDomain", function(url) {
    re = new RegExp('^.*?\/\/([^\/\?\:\#]+)');
    if(re.test(url)) {
	return RegExp.$1;
    }
});

Handlebars.registerHelper("getTime", function(relative_time) {
    relative_time = relative_time.match(/([0-9]+)([a-z]+).*/);
    if(+relative_time[1] > 1) {
	return relative_time[1] + " " + (relative_time[2] === "hr" ? "hrs" : "mins");
    }
    return relative_time[1] + " " + relative_time[2];
});
<commit_msg>News: Actually use the good_stories array when trimming.<commit_after>function ddg_spice_news(api_result) {
    "use strict";

    // Words that we have to skip in DDG.isRelevant.
    var skip = [
        "news",
        "headline",
        "headlines",
        "latest",
        "breaking",
        "update",
        "s:d",
        "sort:date"
    ];

    // Check if the word news is in the query.
    var generic = false;
    if((/\bnews\b/i).test(DDG.get_query())) {
        generic = true;
    }

    // Some sources need to be set by us.
    var getSource = function(story) {
        switch(story.syndicate) {
            case "Topsy":
            story.source = story.author || "Topsy";
            break;
            case "NewsCred":
            if(story.source) {
		if(story.author) {
		    story.source = story.source + " by " + story.author;
		}
	    } else {
		story.source = "NewsCred";
	    }
            break;
        }
    };

    var ellipsis = function(text, limit) {
	var result = [];
	var count = 0;
	var words = text.split(" ");

	for(var i = 0; i < words.length; i++) {
	    count += words[i].length + 1;
	    if(count < limit) {
		result.push(words[i]);
	    }
	}

	if(words.length > result.length &&
	   !(result[result.length - 1].match(/\.$/))) {
	    result.push("...");
	}

	return result.join(" ");
    };

    for(var i = 0, story; story = api_result[i]; i++) {
	story.title = story.title.replace(/<b>|<\/b>|:/g, "");
    }

    var good_stories = [];
    if(generic) {
        good_stories = api_result;
    } else {
        for(var i = 0, story; story = api_result[i]; i++) {
	    if(DDG.isRelevant(story.title, skip, 3)) {
                getSource(story);
                good_stories.push(story);
            }
        }
    }

    // Exit if we didn't get any results.
    if(good_stories.length === 0) {
        return;
    }

    if(good_stories.length > 1) {
	for(var i = 0, story; story = good_stories[i]; i++) {
	    story.title = ellipsis(story.title, 55);
	    story.excerpt = ellipsis(story.excerpt, 130);
	}
    }

    // Display the plugin.
    Spice.render({
        header1: good_stories[0].query +  " (News)",
	source_url: good_stories[0].url,
	source_name: good_stories[0].source,

	spice_name: "news",

	template_frame: "carousel",
	template_options: {
	    items: good_stories,
	    template_item: "news",
	    li_width: 196,
	    li_height: 155
	},

	force_big_header: true,
	force_no_fold: true
    });
}

Handlebars.registerHelper("getIcon", function(url) {
    return Handlebars.helpers.favicon.call({source_url: url, forces: {}});
});

Handlebars.registerHelper("getDomain", function(url) {
    re = new RegExp('^.*?\/\/([^\/\?\:\#]+)');
    if(re.test(url)) {
	return RegExp.$1;
    }
});

Handlebars.registerHelper("getTime", function(relative_time) {
    relative_time = relative_time.match(/([0-9]+)([a-z]+).*/);
    if(+relative_time[1] > 1) {
	return relative_time[1] + " " + (relative_time[2] === "hr" ? "hrs" : "mins");
    }
    return relative_time[1] + " " + relative_time[2];
});
<|endoftext|>


<commit_before>(function(env) {
    env.ddg_spice_is_it_up = function(api_result) {
        "use strict";

        if(!api_result){
            return Spice.failed('is_it_up');
        }

        Spice.add({
            id: 'is_it_up',
            name: 'Answer',
            data: api_result,
            signal: 'high',
            meta: {
                sourceUrl: 'http://isitup.org/' + api_result['domain'],
                sourceName: 'Is it up?',
                sourceIcon: true
            },
            templates: {
                group: 'base',
                options: {
                    content: Spice.is_it_up.detail,
                    moreAt: true
                }
            }
        });
    }

    Handlebars.registerHelper ('if_value', function(a, b, options) {
        if (a == b) {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });
}(this));
<commit_msg>removed bad indent<commit_after>(function(env) {
    env.ddg_spice_is_it_up = function(api_result) {
        "use strict";

        if(!api_result){
            return Spice.failed('is_it_up');
        }

        Spice.add({
            id: 'is_it_up',
            name: 'Answer',
            data: api_result,
            signal: 'high',
            meta: {
                sourceUrl: 'http://isitup.org/' + api_result['domain'],
                sourceName: 'Is it up?',
                sourceIcon: true
            },
            templates: {
                group: 'base',
                options: {
                    content: Spice.is_it_up.detail,
                    moreAt: true
                }
            }
        });
    }

    Handlebars.registerHelper ('if_value', function(a, b, options) {
        if (a == b) {
            return options.fn(this);
        } else {
            return options.inverse(this);
        }
    });
}(this));
<|endoftext|>


<commit_before>// `ddg_spice_sound_cloud` is a callback function that gets
// called when people search for movie titles. An example trigger
// is "sc oppa spacejam style".

(function(root) {
    "use strict";

    var items = [[]],
        query = DDG.get_query().replace(/(sc|sound\s+cloud|soundcloud)\s*/i, "");

    // This function is responsible for calling other functions that
    // process data and display the plugin.
    root.ddg_spice_sound_cloud = function(sound) {
        if(sound && sound.length) {

            items[0] = {
                a: list_of_tracks(sound),
                h: query + " (Sound Cloud)",
                s: "SoundCloud",
                u: "https://soundcloud.com/search?q=" + query,
                f: true,
                force_big_header: true
            };

            // The rendering function is `nra`.
            nra(items, 1, 1);
        }
    };

    function remove(element) {
        element.parentNode.removeChild(element);
    }

    // `hide` is responsible for hiding the list of songs.
    // It makes use of DDG.toggle which can either hide or unhide an element.
    function hide(element) {
        var abstract = d.getElementById("zero_click_abstract");
        DDG.toggle('soundcloud-play', -1);
        var shell = d.createElement('div');
        shell.setAttribute('id', 'soundcloud-stream');

        // It's important to insert the element before (using, well, node.insertBefore)
        // because node.appendChild would put the element below the "More at ..." link.
        var firstChild = abstract.firstChild;
        shell.appendChild(soundcloud(element));
        // The back button removes the embedded player.
        shell.appendChild(link({"href": "javascript:;", "id": "back-button"}, "Back ↩",
            {
                "click":
                    (function(){
                        DDG.toggle('soundcloud-play', '1');
                        abstract.setAttribute("style", "display: block; margin-right: 50px;");
                        remove(shell);
                    })
            }));
        abstract.insertBefore(shell, firstChild);
        // The player can take advantage of the excised margins.
        abstract.setAttribute("style", "margin: 0px !important;");
    }

    // `stream` is responsible for displaying the icons and for adding the events
    // needed to embed SoundCloud's player.
    function stream(element) {
        return link({
            "href": "javascript:;",
            "title": "Listen to " + element.title
            // This uses Glyphicons Halflings included in Twitter Bootstrap.
        }, element.title, {
            "click": (function(){
                hide(element);
            })
        });
    }

    // `list_of_tracks` wraps the list of songs in HTML.
    function list_of_tracks(sound) {
        // We added an ID so that we can hide this element later on.
        var list = d.createElement('div');
        list.setAttribute("id", "soundcloud-play");

        for(var i = 0; i < sound.length && i < 5; i += 1) {
            list.appendChild(list_element(sound[i]));
        }

        return list;
    }

    // `list_element` is an auxiliary for `list_of_tracks`.
    // This will snug everything, the links, the icons, and the artists,
    // in a single `div` element.
    function list_element(element) {
        var div = d.createElement('div'),
            span = d.createElement('span'),
            icon = d.createElement('i'),
            extra_space = d.createElement('span');
        span.innerHTML = " by ";
        extra_space.innerHTML = " ";
        icon.setAttribute('class', 'icon-play-circle');
        icon.setAttribute('style', 'margin 1px 0px 0px 0px;');
        div.appendChild(icon);
        div.appendChild(extra_space);
        div.appendChild(stream(element));
        div.appendChild(span);
        div.appendChild(link({
            "href": element.user.permalink_url
        }, element.user.username, {}));

        return div;
    }

    // `link` is used to create anchor tags.
    function link(attributes, inner, events) {
        var a = d.createElement('a'),
            hasOwn = ({}).hasOwnProperty;

        for(var i in attributes) {
            if(hasOwn.call(attributes, i)) {
                a.setAttribute(i, attributes[i]);
            }
        }
        a.innerHTML = inner;

        for(i in events) {
            if(hasOwn.call(events, i)) {
                a.addEventListener(i, events[i], false);
            }
        }

        return a;
    }

    // Embed Sound Cloud's player in our plugin in an iframe.
    // An optional argument to the URL is `auto_play=true`
    function soundcloud(res) {
        var iframe = d.createElement('iframe');
        iframe.setAttribute('width', '100%');
        iframe.setAttribute('height', '166');
        iframe.setAttribute('scrolling', 'no');
        iframe.setAttribute('frameborder', 'no');
        iframe.setAttribute('src', 'https://w.soundcloud.com/player/?url=' + encodeURI(res.uri) + "&auto_play=true");
        return iframe;
    }
}(this));

<commit_msg>Added 'force_no_fold.'<commit_after>// `ddg_spice_sound_cloud` is a callback function that gets
// called when people search for movie titles. An example trigger
// is "sc oppa spacejam style".

(function(root) {
    "use strict";

    var items = [[]],
        query = DDG.get_query().replace(/(sc|sound\s+cloud|soundcloud)\s*/i, "");

    // This function is responsible for calling other functions that
    // process data and display the plugin.
    root.ddg_spice_sound_cloud = function(sound) {
        if(sound && sound.length) {

            items[0] = {
                a: list_of_tracks(sound),
                h: query + " (Sound Cloud)",
                s: "SoundCloud",
                u: "https://soundcloud.com/search?q=" + query,
                force_no_fold: 1,
                force_big_header: true
            };

            // The rendering function is `nra`.
            nra(items, 1, 1);
        }
    };

    function remove(element) {
        element.parentNode.removeChild(element);
    }

    // `hide` is responsible for hiding the list of songs.
    // It makes use of DDG.toggle which can either hide or unhide an element.
    function hide(element) {
        var abstract = d.getElementById("zero_click_abstract");
        DDG.toggle('soundcloud-play', -1);
        var shell = d.createElement('div');
        shell.setAttribute('id', 'soundcloud-stream');

        // It's important to insert the element before (using, well, node.insertBefore)
        // because node.appendChild would put the element below the "More at ..." link.
        var firstChild = abstract.firstChild;
        shell.appendChild(soundcloud(element));
        // The back button removes the embedded player.
        shell.appendChild(link({"href": "javascript:;", "id": "back-button"}, "Back ↩",
            {
                "click":
                    (function(){
                        DDG.toggle('soundcloud-play', '1');
                        abstract.setAttribute("style", "display: block; margin-right: 50px;");
                        remove(shell);
                    })
            }));
        abstract.insertBefore(shell, firstChild);
        // The player can take advantage of the excised margins.
        abstract.setAttribute("style", "margin: 0px !important;");
    }

    // `stream` is responsible for displaying the icons and for adding the events
    // needed to embed SoundCloud's player.
    function stream(element) {
        return link({
            "href": "javascript:;",
            "title": "Listen to " + element.title
            // This uses Glyphicons Halflings included in Twitter Bootstrap.
        }, element.title, {
            "click": (function(){
                hide(element);
            })
        });
    }

    // `list_of_tracks` wraps the list of songs in HTML.
    function list_of_tracks(sound) {
        // We added an ID so that we can hide this element later on.
        var list = d.createElement('div');
        list.setAttribute("id", "soundcloud-play");

        for(var i = 0; i < sound.length && i < 5; i += 1) {
            list.appendChild(list_element(sound[i]));
        }

        return list;
    }

    // `list_element` is an auxiliary for `list_of_tracks`.
    // This will snug everything, the links, the icons, and the artists,
    // in a single `div` element.
    function list_element(element) {
        var div = d.createElement('div'),
            span = d.createElement('span'),
            icon = d.createElement('i'),
            extra_space = d.createElement('span');
        span.innerHTML = " by ";
        extra_space.innerHTML = " ";
        icon.setAttribute('class', 'icon-play-circle');
        icon.setAttribute('style', 'margin 1px 0px 0px 0px;');
        div.appendChild(icon);
        div.appendChild(extra_space);
        div.appendChild(stream(element));
        div.appendChild(span);
        div.appendChild(link({
            "href": element.user.permalink_url
        }, element.user.username, {}));

        return div;
    }

    // `link` is used to create anchor tags.
    function link(attributes, inner, events) {
        var a = d.createElement('a'),
            hasOwn = ({}).hasOwnProperty;

        for(var i in attributes) {
            if(hasOwn.call(attributes, i)) {
                a.setAttribute(i, attributes[i]);
            }
        }
        a.innerHTML = inner;

        for(i in events) {
            if(hasOwn.call(events, i)) {
                a.addEventListener(i, events[i], false);
            }
        }

        return a;
    }

    // Embed Sound Cloud's player in our plugin in an iframe.
    // An optional argument to the URL is `auto_play=true`
    function soundcloud(res) {
        var iframe = d.createElement('iframe');
        iframe.setAttribute('width', '100%');
        iframe.setAttribute('height', '166');
        iframe.setAttribute('scrolling', 'no');
        iframe.setAttribute('frameborder', 'no');
        iframe.setAttribute('src', 'https://w.soundcloud.com/player/?url=' + encodeURI(res.uri) + "&auto_play=true");
        return iframe;
    }
}(this));

<|endoftext|>


<commit_before>/**
 * jQuery UI PickList Widget
 *
 * Copyright (c) 2012 Awnry Software
 * Distributed under the terms of the MIT License.
 *
 * http://www.awnry.com/
 */
(function($)
{
	$.widget("awnry.pickList",
	{
		options:
		{
			// Container classes
			mainClass:					"pickList",
			listContainerClass:			"pickList_listContainer",
			sourceListContainerClass:	"pickList_sourceListContainer",
			controlsContainerClass:		"pickList_controlsContainer",
			targetListContainerClass:	"pickList_targetListContainer",
			listClass:					"pickList_list",
			sourceListClass:			"pickList_sourceList",
			targetListClass:			"pickList_targetList",
			clearClass:					"pickList_clear",

			// List item classes
			listItemClass:				"pickList_listItem",
			selectedListItemClass:		"pickList_selectedListItem",

			// Control classes
			addAllClass:				"pickList_addAll",
			addClass:					"pickList_add",
			removeAllClass:				"pickList_removeAll",
			removeClass:				"pickList_remove",

			// Control labels
			addAllLabel:				">>",
			addLabel:					">",
			removeAllLabel:				"<<",
			removeLabel:				"<",

			// List labels
			listLabelClass:				"pickList_listLabel",
			sourceListLabel:			"Available",
			sourceListLabelClass:		"pickList_sourceListLabel",
			targetListLabel:			"Selected",
			targetListLabelClass:		"pickList_targetListLabel",

			// Behavior
			sortItems:					true
		},

		_create: function()
		{
			var self = this;

			self._buildPickList();
			self._refresh();
		},

		_buildPickList: function()
		{
			var self = this;

			self.pickList = $("<div/>")
					.hide()
					.addClass(self.options.mainClass)
					.insertAfter(self.element)
					.append(self._buildSourceList())
					.append(self._buildControls())
					.append(self._buildTargetList())
					.append( $("<div/>").addClass(self.options.clearClass) );

			self._populateLists();

			self.element.hide();
			self.pickList.show();
		},

		_buildSourceList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.sourceListContainerClass);

			var label = $("<div/>")
					.text(self.options.sourceListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.sourceListLabelClass);

			self.sourceList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.sourceListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.sourceList);

			return container;
		},

		_buildTargetList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.targetListContainerClass);

			var label = $("<div/>")
					.text(self.options.targetListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.targetListLabelClass);

			self.targetList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.targetListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.targetList);

			return container;
		},

		_buildControls: function()
		{
			var self = this;

			self.controls = $("<div/>").addClass(self.options.controlsContainerClass);

			self.addAllButton = $("<button/>").button().click({pickList: self}, self._addAllHandler).html(self.options.addAllLabel).addClass(self.options.addAllClass);
			self.addButton = $("<button/>").button().click({pickList: self}, self._addHandler).html(self.options.addLabel).addClass(self.options.addClass);
			self.removeButton = $("<button/>").button().click({pickList: self}, self._removeHandler).html(self.options.removeLabel).addClass(self.options.removeClass);
			self.removeAllButton = $("<button/>").button().click({pickList: self}, self._removeAllHandler).html(self.options.removeAllLabel).addClass(self.options.removeAllClass);

			self.controls
					.append(self.addAllButton)
					.append(self.addButton)
					.append(self.removeButton)
					.append(self.removeAllButton);

			return self.controls;
		},

		_populateLists: function()
		{
			var self = this;

			self.element.children().each(function()
			{
				var text = $(this).text();
				var copy = $("<li/>")
						.text(text)
						.val($(this).val())
						.addClass(self.options.listItemClass);

				if($(this).attr("selected") == "selected")
				{
					self.targetList.append( copy );
				}
				else
				{
					self.sourceList.append( copy );
				}
			});
		},

		_addItem: function(value)
		{
			var self = this;

			self.sourceList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.targetList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).attr("selected", "selected");
				}
			});
		},

		_removeItem: function(value)
		{
			var self = this;

			self.targetList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.sourceList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).removeAttr("selected");
				}
			});
		},

		_addAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				self._addItem( $(this).val() );
			});

			self._refresh();
		},

		_addHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._addItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._removeItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				self._removeItem( $(this).val() );
			});

			self._refresh();
		},

		_refresh: function()
		{
			var self = this;

			// Refresh the states of the Add All and Remove All buttons.
			self.addAllButton.button( (self.sourceList.children().length > 0) ? "enable" : "disable" );
			self.removeAllButton.button( (self.targetList.children().length > 0) ? "enable" : "disable" );

			// Refresh the Add button state.
			var sourceSelected = false;
			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					sourceSelected = true;
				}
			});
			self.addButton.button( sourceSelected ? "enable" : "disable" );

			// Refresh the Remove button state.
			var targetSelected = false;
			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					targetSelected = true;
				}
			});
			self.removeButton.button( targetSelected ? "enable" : "disable" );

			// Refresh the buttons.
			self.controls.children().each(function()
			{
				$(this).button("refresh");
			});

			// Sort the selection lists.
			if(self.options.sortItems)
			{
				self._sortItems(self.sourceList);
				self._sortItems(self.targetList);
			}
		},

		_sortItems: function(list)
		{
			var items = new Array();

			list.children().each(function()
			{
				items.push( $(this) );
			});

			items.sort(function(a, b)
			{
				if(a.text() > b.text())
				{
					return 1;
				}
				else if(a.text() == b.text())
				{
					return 0;
				}
				else
				{
					return -1;
				}
			});

			list.empty();

			for(var i = 0; i < items.length; i++)
			{
				list.append(items[i]);
			}
		},

		_changeHandler: function(e)
		{
			var self = e.data.pickList;

			if(e.ctrlKey)
			{
				if(self._isSelected( $(this) ))
				{
					self._removeSelection( $(this) );
				}
				else
				{
					self._addSelection( $(this) );
				}
			}
			else
			{
				$(this).parent().children().each(function()
				{
					self._removeSelection( $(this) );
				});

				self._addSelection( $(this) );
			}

			self._refresh();
		},

		_isSelected: function(listItem)
		{
			return listItem.hasClass("ui-selected");
		},

		_addSelection: function(listItem)
		{
			var self = this;

			return listItem
					.addClass("ui-selected")
					.addClass("ui-state-highlight")
					.addClass(self.options.selectedListItemClass);
		},

		_removeSelection: function(listItem)
		{
			var self = this;

			return listItem
					.removeClass("ui-selected")
					.removeClass("ui-state-highlight")
					.removeClass(self.options.selectedListItemClass);
		},

		_setOption: function(key, value)
		{
			switch(key)
			{
				case "clear":
				{
					break;
				}
			}

			$.Widget.prototype._setOption.apply(this, arguments);
		},

		destroy: function()
		{
			$.Widget.prototype.destroy.call(this);
		}
	});
}(jQuery));<commit_msg>Disable text selection on the list container, so in Internet Explorer CTRL+click does not cause text to be selected by accident.<commit_after>/**
 * jQuery UI PickList Widget
 *
 * Copyright (c) 2012 Awnry Software
 * Distributed under the terms of the MIT License.
 *
 * http://www.awnry.com/
 */
(function($)
{
	$.widget("awnry.pickList",
	{
		options:
		{
			// Container classes
			mainClass:					"pickList",
			listContainerClass:			"pickList_listContainer",
			sourceListContainerClass:	"pickList_sourceListContainer",
			controlsContainerClass:		"pickList_controlsContainer",
			targetListContainerClass:	"pickList_targetListContainer",
			listClass:					"pickList_list",
			sourceListClass:			"pickList_sourceList",
			targetListClass:			"pickList_targetList",
			clearClass:					"pickList_clear",

			// List item classes
			listItemClass:				"pickList_listItem",
			selectedListItemClass:		"pickList_selectedListItem",

			// Control classes
			addAllClass:				"pickList_addAll",
			addClass:					"pickList_add",
			removeAllClass:				"pickList_removeAll",
			removeClass:				"pickList_remove",

			// Control labels
			addAllLabel:				">>",
			addLabel:					">",
			removeAllLabel:				"<<",
			removeLabel:				"<",

			// List labels
			listLabelClass:				"pickList_listLabel",
			sourceListLabel:			"Available",
			sourceListLabelClass:		"pickList_sourceListLabel",
			targetListLabel:			"Selected",
			targetListLabelClass:		"pickList_targetListLabel",

			// Behavior
			sortItems:					true
		},

		_create: function()
		{
			var self = this;

			self._buildPickList();
			self._refresh();
		},

		_buildPickList: function()
		{
			var self = this;

			self.pickList = $("<div/>")
					.hide()
					.addClass(self.options.mainClass)
					.insertAfter(self.element)
					.append(self._buildSourceList())
					.append(self._buildControls())
					.append(self._buildTargetList())
					.append( $("<div/>").addClass(self.options.clearClass) );

			self._populateLists();

			self.element.hide();
			self.pickList.show();
		},

		_buildSourceList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.sourceListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.sourceListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.sourceListLabelClass);

			self.sourceList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.sourceListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.sourceList);

			return container;
		},

		_buildTargetList: function()
		{
			var self = this;

			var container = $("<div/>")
					.addClass(self.options.listContainerClass)
					.addClass(self.options.targetListContainerClass)
					.disableSelection();

			var label = $("<div/>")
					.text(self.options.targetListLabel)
					.addClass(self.options.listLabelClass)
					.addClass(self.options.targetListLabelClass);

			self.targetList = $("<ul/>")
					.addClass(self.options.listClass)
					.addClass(self.options.targetListClass)
					.delegate("li", "click", {pickList: self}, self._changeHandler);

			container
					.append(label)
					.append(self.targetList);

			return container;
		},

		_buildControls: function()
		{
			var self = this;

			self.controls = $("<div/>").addClass(self.options.controlsContainerClass);

			self.addAllButton = $("<button/>").button().click({pickList: self}, self._addAllHandler).html(self.options.addAllLabel).addClass(self.options.addAllClass);
			self.addButton = $("<button/>").button().click({pickList: self}, self._addHandler).html(self.options.addLabel).addClass(self.options.addClass);
			self.removeButton = $("<button/>").button().click({pickList: self}, self._removeHandler).html(self.options.removeLabel).addClass(self.options.removeClass);
			self.removeAllButton = $("<button/>").button().click({pickList: self}, self._removeAllHandler).html(self.options.removeAllLabel).addClass(self.options.removeAllClass);

			self.controls
					.append(self.addAllButton)
					.append(self.addButton)
					.append(self.removeButton)
					.append(self.removeAllButton);

			return self.controls;
		},

		_populateLists: function()
		{
			var self = this;

			self.element.children().each(function()
			{
				var text = $(this).text();
				var copy = $("<li/>")
						.text(text)
						.val($(this).val())
						.addClass(self.options.listItemClass);

				if($(this).attr("selected") == "selected")
				{
					self.targetList.append( copy );
				}
				else
				{
					self.sourceList.append( copy );
				}
			});
		},

		_addItem: function(value)
		{
			var self = this;

			self.sourceList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.targetList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).attr("selected", "selected");
				}
			});
		},

		_removeItem: function(value)
		{
			var self = this;

			self.targetList.children().each(function()
			{
				if($(this).val() == value)
				{
					self.sourceList.append( self._removeSelection($(this)) );
				}
			});

			self.element.children().each(function()
			{
				if($(this).val() == value)
				{
					$(this).removeAttr("selected");
				}
			});
		},

		_addAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				self._addItem( $(this).val() );
			});

			self._refresh();
		},

		_addHandler: function(e)
		{
			var self = e.data.pickList;

			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._addItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					self._removeItem( $(this).val() );
				}
			});

			self._refresh();
		},

		_removeAllHandler: function(e)
		{
			var self = e.data.pickList;

			self.targetList.children().each(function()
			{
				self._removeItem( $(this).val() );
			});

			self._refresh();
		},

		_refresh: function()
		{
			var self = this;

			// Refresh the states of the Add All and Remove All buttons.
			self.addAllButton.button( (self.sourceList.children().length > 0) ? "enable" : "disable" );
			self.removeAllButton.button( (self.targetList.children().length > 0) ? "enable" : "disable" );

			// Refresh the Add button state.
			var sourceSelected = false;
			self.sourceList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					sourceSelected = true;
				}
			});
			self.addButton.button( sourceSelected ? "enable" : "disable" );

			// Refresh the Remove button state.
			var targetSelected = false;
			self.targetList.children().each(function()
			{
				if(self._isSelected( $(this) ))
				{
					targetSelected = true;
				}
			});
			self.removeButton.button( targetSelected ? "enable" : "disable" );

			// Refresh the buttons.
			self.controls.children().each(function()
			{
				$(this).button("refresh");
			});

			// Sort the selection lists.
			if(self.options.sortItems)
			{
				self._sortItems(self.sourceList);
				self._sortItems(self.targetList);
			}
		},

		_sortItems: function(list)
		{
			var items = new Array();

			list.children().each(function()
			{
				items.push( $(this) );
			});

			items.sort(function(a, b)
			{
				if(a.text() > b.text())
				{
					return 1;
				}
				else if(a.text() == b.text())
				{
					return 0;
				}
				else
				{
					return -1;
				}
			});

			list.empty();

			for(var i = 0; i < items.length; i++)
			{
				list.append(items[i]);
			}
		},

		_changeHandler: function(e)
		{
			var self = e.data.pickList;

			if(e.ctrlKey)
			{
				if(self._isSelected( $(this) ))
				{
					self._removeSelection( $(this) );
				}
				else
				{
					self._addSelection( $(this) );
				}
			}
			else
			{
				$(this).parent().children().each(function()
				{
					self._removeSelection( $(this) );
				});

				self._addSelection( $(this) );
			}

			self._refresh();
		},

		_isSelected: function(listItem)
		{
			return listItem.hasClass("ui-selected");
		},

		_addSelection: function(listItem)
		{
			var self = this;

			return listItem
					.addClass("ui-selected")
					.addClass("ui-state-highlight")
					.addClass(self.options.selectedListItemClass);
		},

		_removeSelection: function(listItem)
		{
			var self = this;

			return listItem
					.removeClass("ui-selected")
					.removeClass("ui-state-highlight")
					.removeClass(self.options.selectedListItemClass);
		},

		_setOption: function(key, value)
		{
			switch(key)
			{
				case "clear":
				{
					break;
				}
			}

			$.Widget.prototype._setOption.apply(this, arguments);
		},

		destroy: function()
		{
			$.Widget.prototype.destroy.call(this);
		}
	});
}(jQuery));<|endoftext|>


<commit_before>// @flow
import {makeRouteDefNode, makeLeafTags} from '../route-tree'
import {isMobile} from '../constants/platform'

const profileRoute = () => {
  const pgpRoutes = require('./pgp/routes').default
  const Profile = require('./user/container').default
  const AddToTeam = require('./add-to-team/container').default
  const EditProfile = require('./edit-profile/container').default
  const EditAvatar = require('./edit-avatar/container').default
  const ProveEnterUsername = require('./prove-enter-username/container').default
  const ProveWebsiteChoice = require('./prove-website-choice/container').default
  const RevokeContainer = require('./revoke/container').default
  const PostProof = require('./post-proof/container').default
  const ConfirmOrPending = require('./confirm-or-pending/container').default
  const SearchPopup = require('./search/container').default
  const NonUserProfile = require('./non-user-profile/container').default
  const ShowcaseTeamOffer = require('./showcase-team-offer/container').default
  const ControlledRolePicker = require('../teams/role-picker/controlled-container').default
  const WalletConstants = require('../constants/wallets')
  const ProofsList = require('./generic/proofs-list/container').default
  const GenericEnterUsername = require('./generic/enter-username/container').default
  const GenericProofSuccess = require('./generic/success/container').default

  const SendRequestFormRoutes = require('../wallets/routes-send-request-form').default()

  const proveEnterUsername = makeRouteDefNode({
    children: {
      profileConfirmOrPending: {component: ConfirmOrPending},
      profilePostProof: {
        children: {
          profileConfirmOrPending: {component: ConfirmOrPending},
        },
        component: PostProof,
      },
    },
    component: ProveEnterUsername,
  })

  const profileGenericEnterUsername = {
    children: {
      profileGenericProofSuccess: {
        component: GenericProofSuccess,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
    },
    component: GenericEnterUsername,
    tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
  }

  return makeRouteDefNode({
    children: {
      addToTeam: {
        children: {
          teamControlledRolePicker: {
            children: {},
            component: ControlledRolePicker,
            tags: makeLeafTags({fullscreen: isMobile, layerOnTop: !isMobile}),
          },
        },
        component: AddToTeam,
        tags: makeLeafTags({fullscreen: isMobile, layerOnTop: !isMobile}),
      },
      profile: profileRoute,
      profileEdit: {
        component: EditProfile,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
      profileEditAvatar: {
        component: EditAvatar,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      profileGenericEnterUsername,
      profileNonUser: {
        children: {profile: profileRoute},
        component: NonUserProfile,
      },
      profilePgp: pgpRoutes,
      profileProofsList: {
        children: {
          profileGenericEnterUsername,
        },
        component: ProofsList,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
      profileProveEnterUsername: proveEnterUsername,
      profileProveWebsiteChoice: {
        children: {proveEnterUsername},
        component: ProveWebsiteChoice,
      },
      profileRevoke: {component: RevokeContainer},
      profileSearch: {
        children: {},
        component: SearchPopup,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      profileShowcaseTeamOffer: {
        children: {},
        component: ShowcaseTeamOffer,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      [WalletConstants.sendRequestFormRouteKey]: SendRequestFormRoutes,
    },
    component: Profile,
    initialState: {currentFriendshipsTab: 'Followers'},
    tags: makeLeafTags({title: 'Profile', underNotch: true}),
  })
}

export const newRoutes = {
  profile: {getScreen: () => require('./user/container').default, upgraded: true},
  profileNonUser: {getScreen: () => require('./non-user-profile/container').default},
}

export const newModalRoutes = {
  profileAddToTeam: {getScreen: () => require('./add-to-team/container').default, upgraded: true},
  profileConfirmOrPending: {
    getScreen: () => require('./confirm-or-pending/container').default,
    upgraded: true,
  },
  profileEdit: {getScreen: () => require('./edit-profile/container').default},
  profileEditAvatar: {getScreen: () => require('./edit-avatar/container').default, upgraded: true},
  profileGenericEnterUsername: {
    getScreen: () => require('./generic/enter-username/container').default,
    upgraded: true,
  },
  profileGenericProofSuccess: {
    getScreen: () => require('./generic/success/container').default,
    upgraded: true,
  },
  profilePostProof: {getScreen: () => require('./post-proof/container').default, upgraded: true},
  profileProofsList: {getScreen: () => require('./generic/proofs-list/container').default, upgraded: true},
  profileProveEnterUsername: {
    getScreen: () => require('./prove-enter-username/container').default,
    upgraded: true,
  },
  profileProveWebsiteChoice: {
    getScreen: () => require('./prove-website-choice/container').default,
    upgraded: true,
  },
  profileRevoke: {getScreen: () => require('./revoke/container').default, upgraded: true},
  profileSearch: {getScreen: () => require('./search/container').default},
  profileShowcaseTeamOffer: {
    getScreen: () => require('./showcase-team-offer/container').default,
    upgraded: true,
  },
  teamControlledRolePicker: {getScreen: () => require('../teams/role-picker/controlled-container').default},
  ...require('./pgp/routes').newRoutes,
}

export default profileRoute
<commit_msg>Fix "Profile->Add to team.." on nav1 (#16910)<commit_after>// @flow
import {makeRouteDefNode, makeLeafTags} from '../route-tree'
import {isMobile} from '../constants/platform'

const profileRoute = () => {
  const pgpRoutes = require('./pgp/routes').default
  const Profile = require('./user/container').default
  const AddToTeam = require('./add-to-team/container').default
  const EditProfile = require('./edit-profile/container').default
  const EditAvatar = require('./edit-avatar/container').default
  const ProveEnterUsername = require('./prove-enter-username/container').default
  const ProveWebsiteChoice = require('./prove-website-choice/container').default
  const RevokeContainer = require('./revoke/container').default
  const PostProof = require('./post-proof/container').default
  const ConfirmOrPending = require('./confirm-or-pending/container').default
  const SearchPopup = require('./search/container').default
  const NonUserProfile = require('./non-user-profile/container').default
  const ShowcaseTeamOffer = require('./showcase-team-offer/container').default
  const ControlledRolePicker = require('../teams/role-picker/controlled-container').default
  const WalletConstants = require('../constants/wallets')
  const ProofsList = require('./generic/proofs-list/container').default
  const GenericEnterUsername = require('./generic/enter-username/container').default
  const GenericProofSuccess = require('./generic/success/container').default

  const SendRequestFormRoutes = require('../wallets/routes-send-request-form').default()

  const proveEnterUsername = makeRouteDefNode({
    children: {
      profileConfirmOrPending: {component: ConfirmOrPending},
      profilePostProof: {
        children: {
          profileConfirmOrPending: {component: ConfirmOrPending},
        },
        component: PostProof,
      },
    },
    component: ProveEnterUsername,
  })

  const profileGenericEnterUsername = {
    children: {
      profileGenericProofSuccess: {
        component: GenericProofSuccess,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
    },
    component: GenericEnterUsername,
    tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
  }

  return makeRouteDefNode({
    children: {
      profile: profileRoute,
      profileAddToTeam: {
        children: {
          teamControlledRolePicker: {
            children: {},
            component: ControlledRolePicker,
            tags: makeLeafTags({fullscreen: isMobile, layerOnTop: !isMobile}),
          },
        },
        component: AddToTeam,
        tags: makeLeafTags({fullscreen: isMobile, layerOnTop: !isMobile}),
      },
      profileEdit: {
        component: EditProfile,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
      profileEditAvatar: {
        component: EditAvatar,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      profileGenericEnterUsername,
      profileNonUser: {
        children: {profile: profileRoute},
        component: NonUserProfile,
      },
      profilePgp: pgpRoutes,
      profileProofsList: {
        children: {
          profileGenericEnterUsername,
        },
        component: ProofsList,
        tags: makeLeafTags({layerOnTop: !isMobile, renderTopmostOnly: true}),
      },
      profileProveEnterUsername: proveEnterUsername,
      profileProveWebsiteChoice: {
        children: {proveEnterUsername},
        component: ProveWebsiteChoice,
      },
      profileRevoke: {component: RevokeContainer},
      profileSearch: {
        children: {},
        component: SearchPopup,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      profileShowcaseTeamOffer: {
        children: {},
        component: ShowcaseTeamOffer,
        tags: makeLeafTags({layerOnTop: !isMobile}),
      },
      [WalletConstants.sendRequestFormRouteKey]: SendRequestFormRoutes,
    },
    component: Profile,
    initialState: {currentFriendshipsTab: 'Followers'},
    tags: makeLeafTags({title: 'Profile', underNotch: true}),
  })
}

export const newRoutes = {
  profile: {getScreen: () => require('./user/container').default, upgraded: true},
  profileNonUser: {getScreen: () => require('./non-user-profile/container').default},
}

export const newModalRoutes = {
  profileAddToTeam: {getScreen: () => require('./add-to-team/container').default, upgraded: true},
  profileConfirmOrPending: {
    getScreen: () => require('./confirm-or-pending/container').default,
    upgraded: true,
  },
  profileEdit: {getScreen: () => require('./edit-profile/container').default},
  profileEditAvatar: {getScreen: () => require('./edit-avatar/container').default, upgraded: true},
  profileGenericEnterUsername: {
    getScreen: () => require('./generic/enter-username/container').default,
    upgraded: true,
  },
  profileGenericProofSuccess: {
    getScreen: () => require('./generic/success/container').default,
    upgraded: true,
  },
  profilePostProof: {getScreen: () => require('./post-proof/container').default, upgraded: true},
  profileProofsList: {getScreen: () => require('./generic/proofs-list/container').default, upgraded: true},
  profileProveEnterUsername: {
    getScreen: () => require('./prove-enter-username/container').default,
    upgraded: true,
  },
  profileProveWebsiteChoice: {
    getScreen: () => require('./prove-website-choice/container').default,
    upgraded: true,
  },
  profileRevoke: {getScreen: () => require('./revoke/container').default, upgraded: true},
  profileSearch: {getScreen: () => require('./search/container').default},
  profileShowcaseTeamOffer: {
    getScreen: () => require('./showcase-team-offer/container').default,
    upgraded: true,
  },
  teamControlledRolePicker: {getScreen: () => require('../teams/role-picker/controlled-container').default},
  ...require('./pgp/routes').newRoutes,
}

export default profileRoute
<|endoftext|>


<commit_before>/**
 * @fileOverview Grabs the data URL for this application and sends it to the
 * extension to make a cross-domain request for content.
 */

/**
 * @namespace
 */
var privlyParameters = {

  /**
   * Converts a string to an associative array. You generally only want to use
   * `getParameters()`
   *
   * @param string parameter_string String containing parameters and no anchor
   * text.
   *
   * @return object
   */
  parameterStringToHash: function(parameterString) {
    var parameterHash = {};
    var parameterArray = parameterString.split("&");
    for (var i = 0; i < parameterArray.length; i++) {
      var pair = parameterArray[i].split("=");
      var key = decodeURIComponent(pair[0]);
      var value = decodeURIComponent(pair[1]);
      parameterHash[key] = value;
    }

    return parameterHash;
  },

  /**
   * Get an associative array of the parameters found in both the anchor and
   * the parameter string.
   *
   * @param {string} url The original href for the currently injected
   * application.
   *
   * @return {object} An associative array of parameters derived from the url.
   **/
  getParameterHash: function(url)
  {
    var parameters = {};
    var hashIndex = url.indexOf("#");
    if (hashIndex >= 0) {
      parameters = privlyParameters.parameterStringToHash(url.substring(hashIndex + 1));
    }
    var paramIndex = url.indexOf("?");
    if (paramIndex >= 0) {
      var param2 = {};
      if( hashIndex >= 0 ) {
        param2 = privlyParameters.parameterStringToHash(url.substring(paramIndex + 1, hashIndex));
      } else {
        param2 = privlyParameters.parameterStringToHash(url.substr(paramIndex + 1));
      }
      for (var attrname in param2) {
        if (param2.hasOwnProperty(attrname)) {
          parameters[attrname] = param2[attrname];
        }
    }
    return parameters;
  },

  /**
   * Gets the URL the application is tied to. We need this because the
   * application may not be served from the server where it is normally
   * hosted.
   *
   * @return string the address of the application.
   */
  getApplicationUrl: function(url) {

    //handles the Chrome platform
    if (url.indexOf("privlyOriginalURL=") >= 0) {
      url = decodeURIComponent(url);
      return url.substr(url.indexOf("privlyOriginalURL=") + 18);
    }
    return url;
  },

  /**
   * Converts an associative array to an encoded string
   * for appending to the anchor.
   *
   * @param object associativeArray Object to be serialized
   * @return string
   */
  objectToParameterString: function(associativeArray) {
    var parameterString = "";
    for (var key in associativeArray)
    {
        if( parameterString === "" )
        {
          parameterString = encodeURIComponent(key);
          parameterString += "=" + encodeURIComponent(associativeArray[key]);
        } else {
          parameterString += "&" + encodeURIComponent(key);
          parameterString += "=" + encodeURIComponent(associativeArray[key]);
        }
    }

    return parameterString;
  }
};
<commit_msg>Correct syntax<commit_after>/**
 * @fileOverview Grabs the data URL for this application and sends it to the
 * extension to make a cross-domain request for content.
 */

/**
 * @namespace
 */
var privlyParameters = {

  /**
   * Converts a string to an associative array. You generally only want to use
   * `getParameters()`
   *
   * @param string parameter_string String containing parameters and no anchor
   * text.
   *
   * @return object
   */
  parameterStringToHash: function(parameterString) {
    var parameterHash = {};
    var parameterArray = parameterString.split("&");
    for (var i = 0; i < parameterArray.length; i++) {
      var pair = parameterArray[i].split("=");
      var key = decodeURIComponent(pair[0]);
      var value = decodeURIComponent(pair[1]);
      parameterHash[key] = value;
    }

    return parameterHash;
  },

  /**
   * Get an associative array of the parameters found in both the anchor and
   * the parameter string.
   *
   * @param {string} url The original href for the currently injected
   * application.
   *
   * @return {object} An associative array of parameters derived from the url.
   **/
  getParameterHash: function(url)
  {
    var parameters = {};
    var hashIndex = url.indexOf("#");
    if (hashIndex >= 0) {
      parameters = privlyParameters.parameterStringToHash(url.substring(hashIndex + 1));
    }
    var paramIndex = url.indexOf("?");
    if (paramIndex >= 0) {
      var param2 = {};
      if( hashIndex >= 0 ) {
        param2 = privlyParameters.parameterStringToHash(url.substring(paramIndex + 1, hashIndex));
      } else {
        param2 = privlyParameters.parameterStringToHash(url.substr(paramIndex + 1));
      }
      for (var attrname in param2) {
        if (param2.hasOwnProperty(attrname)) {
          parameters[attrname] = param2[attrname];
        }
      }
    }
    return parameters;
  },

  /**
   * Gets the URL the application is tied to. We need this because the
   * application may not be served from the server where it is normally
   * hosted.
   *
   * @return string the address of the application.
   */
  getApplicationUrl: function(url) {

    //handles the Chrome platform
    if (url.indexOf("privlyOriginalURL=") >= 0) {
      url = decodeURIComponent(url);
      return url.substr(url.indexOf("privlyOriginalURL=") + 18);
    }
    return url;
  },

  /**
   * Converts an associative array to an encoded string
   * for appending to the anchor.
   *
   * @param object associativeArray Object to be serialized
   * @return string
   */
  objectToParameterString: function(associativeArray) {
    var parameterString = "";
    for (var key in associativeArray)
    {
        if( parameterString === "" )
        {
          parameterString = encodeURIComponent(key);
          parameterString += "=" + encodeURIComponent(associativeArray[key]);
        } else {
          parameterString += "&" + encodeURIComponent(key);
          parameterString += "=" + encodeURIComponent(associativeArray[key]);
        }
    }

    return parameterString;
  }
};
<|endoftext|>


<commit_before>/* Flot plugin for drawing all elements of a plot on the canvas.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

Flot normally produces certain elements, like axis labels and the legend, using
HTML elements. This permits greater interactivity and customization, and often
looks better, due to cross-browser canvas text inconsistencies and limitations.

It can also be desirable to render the plot entirely in canvas, particularly
if the goal is to save it as an image, or if Flot is being used in a context
where the HTML DOM does not exist, as is the case within Node.js. This plugin
switches out Flot's standard drawing operations for canvas-only replacements.

Currently the plugin supports only axis labels, but it will eventually allow
every element of the plot to be rendered directly to canvas.

The plugin supports these options:

{
    canvas: boolean
}

The "canvas" option controls whether full canvas drawing is enabled, making it
possible to toggle on and off. This is useful when a plot uses HTML text in the
browser, but needs to redraw with canvas text when exporting as an image.

*/

(function($) {

	var options = {
		canvas: true
	};

	var render, getTextInfo, addText;

	// Cache the prototype hasOwnProperty for faster access

	var hasOwnProperty = Object.prototype.hasOwnProperty;

	function init(plot, classes) {

		var Canvas = classes.Canvas;

		// We only want to replace the functions once; the second time around
		// we would just get our new function back.  This whole replacing of
		// prototype functions is a disaster, and needs to be changed ASAP.

		if (render == null) {
			getTextInfo = Canvas.prototype.getTextInfo,
			addText = Canvas.prototype.addText,
			render = Canvas.prototype.render;
		}

		// Finishes rendering the canvas, including overlaid text

		Canvas.prototype.render = function() {

			if (!plot.getOptions().canvas) {
				return render.call(this);
			}

			var context = this.context,
				cache = this._textCache;

			// For each text layer, render elements marked as active

			context.save();

			for (var layerKey in cache) {
				if (hasOwnProperty.call(cache, layerKey)) {
					var layerCache = cache[layerKey];
					for (var styleKey in layerCache) {
						if (hasOwnProperty.call(layerCache, styleKey)) {
							var styleCache = layerCache[styleKey],
								updateStyles = true;
							for (var key in styleCache) {
								if (hasOwnProperty.call(styleCache, key)) {

									var info = styleCache[key];

									if (!info.active) {
										delete styleCache[key];
										continue;
									}

									var x = info.x,
										y = info.y,
										lines = info.lines,
										halign = info.halign;

									// Since every element at this level of the cache have the
									// same font and fill styles, we can just change them once
									// using the values from the first element.

									if (updateStyles) {
										context.fillStyle = info.font.color;
										context.font = info.font.definition;
										updateStyles = false;
									}

									// TODO: Comments in Ole's implementation indicate that
									// some browsers differ in their interpretation of 'top';
									// so far I don't see this, but it requires more testing.
									// We'll stick with top until this can be verified.

									// Original comment was:
									// Top alignment would be more natural, but browsers can
									// differ a pixel or two in where they consider the top to
									// be, so instead we middle align to minimize variation
									// between browsers and compensate when calculating the
									// coordinates.

									context.textBaseline = "top";

									for (var i = 0; i < lines.length; ++i) {

										var line = lines[i],
											linex = x;

										// Apply horizontal alignment per-line

										if (halign == "center") {
											linex -= line.width / 2;
										} else if (halign == "right") {
											linex -= line.width;
										}

										// FIXME: LEGACY BROWSER FIX
										// AFFECTS: Opera < 12.00

										// Round the coordinates, since Opera otherwise
										// switches to uglier (probably non-hinted) rendering.
										// Also offset the y coordinate, since Opera is off
										// pretty consistently compared to the other browsers.

										if (!!(window.opera && window.opera.version().split(".")[0] < 12)) {
											linex = Math.floor(linex);
											y = Math.ceil(y - 2);
										}

										context.fillText(line.text, Math.round(linex), Math.round(y));
										y += line.height;
									}
								}
							}
						}
					}
				}
			}

			context.restore();
		};

		// Creates (if necessary) and returns a text info object.
		//
		// When the canvas option is set, the object looks like this:
		//
		// {
		//     x: X coordinate at which the text is located.
		//     x: Y coordinate at which the text is located.
		//     width: Width of the text's bounding box.
		//     height: Height of the text's bounding box.
		//     active: Flag indicating whether the text should be visible.
		//     lines: [{
		//         height: Height of this line.
		//         widths: Width of this line.
		//         text: Text on this line.
		//     }],
		//     font: {
		//         definition: Canvas font property string.
		//         color: Color of the text.
		//     },
		// }

		Canvas.prototype.getTextInfo = function(layer, text, font, angle) {

			if (!plot.getOptions().canvas) {
				return getTextInfo.call(this, layer, text, font, angle);
			}

			var textStyle, layerCache, styleCache, info;

			// Cast the value to a string, in case we were given a number

			text = "" + text;

			// If the font is a font-spec object, generate a CSS definition

			if (typeof font === "object") {
				textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;
			} else {
				textStyle = font;
			}

			// Retrieve (or create) the cache for the text's layer and styles

			layerCache = this._textCache[layer];

			if (layerCache == null) {
				layerCache = this._textCache[layer] = {};
			}

			styleCache = layerCache[textStyle];

			if (styleCache == null) {
				styleCache = layerCache[textStyle] = {};
			}

			info = styleCache[text];

			if (info == null) {

				var context = this.context;

				// If the font was provided as CSS, create a div with those
				// classes and examine it to generate a canvas font spec.

				// Note the trick of using a line-height of 1, without units;
				// this sets it equal to the font-size, even if the font-size
				// is something abstract, like 'smaller'.  This enables us to
				// read the real font-size via the element's height, working
				// around browsers that return the literal 'smaller' value.

				if (typeof font !== "object") {

					var element = $("

")
						.addClass(typeof font === "string" ? font : null)
						.css({ position: "absolute", padding: 0, 'line-height': 1 })
						.appendTo(this.getTextLayer(layer));

					font = {
						style: element.css("font-style"),
						variant: element.css("font-variant"),
						weight: element.css("font-weight"),
						size: element.height(),
						family: element.css("font-family"),
						color: element.css("color")
					};

					element.remove();
				}

				textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;

				// Create a new info object, initializing the dimensions to
				// zero so we can count them up line-by-line.

				info = styleCache[text] = {
					x: null,
					y: null,
					width: 0,
					height: 0,
					active: false,
					lines: [],
					font: {
						definition: textStyle,
						color: font.color
					}
				};

				context.save();
				context.font = textStyle;

				// Canvas can't handle multi-line strings; break on various
				// newlines, including HTML brs, to build a list of lines.
				// Note that we could split directly on regexps, but IE < 9 is
				// broken; revisit when we drop IE 7/8 support.

				var lines = (text + "").replace(/
|\r\n|\r/g, "\n").split("\n");

				for (var i = 0; i < lines.length; ++i) {

					var lineText = lines[i],
						measured = context.measureText(lineText),
						lineWidth, lineHeight;

					lineWidth = measured.width;

					// Height might not be defined; not in the standard yet

					lineHeight = measured.height || font.size;

					// Add a bit of margin since font rendering is not pixel
					// perfect and cut off letters look bad.  This also doubles
					// as spacing between lines.

					lineHeight += Math.round(font.size * 0.15);

					info.width = Math.max(lineWidth, info.width);
					info.height += lineHeight;

					info.lines.push({
						text: lineText,
						width: lineWidth,
						height: lineHeight
					});
				}

				context.restore();
			}

			return info;
		};

		// Adds a text string to the canvas text overlay.

		Canvas.prototype.addText = function(layer, x, y, text, font, angle, halign, valign) {

			if (!plot.getOptions().canvas) {
				return addText.call(this, layer, x, y, text, font, angle, halign, valign);
			}

			var info = this.getTextInfo(layer, text, font, angle);

			// Mark the text for inclusion in the next render pass

			info.active = true;

			// Save horizontal alignment for later; we'll apply it per-line

			info.x = x;
			info.halign = halign;

			// Tweak the initial y-position to match vertical alignment

			if (valign == "middle") {
				info.y = y - info.height / 2;
			} else if (valign == "bottom") {
				info.y = y - info.height;
			} else {
				info.y = y;
			}
		};
	}

	$.plot.plugins.push({
		init: init,
		options: options,
		name: "canvas",
		version: "1.0"
	});

})(jQuery);
<commit_msg>Set the text baseline only once.<commit_after>/* Flot plugin for drawing all elements of a plot on the canvas.

Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.

Flot normally produces certain elements, like axis labels and the legend, using
HTML elements. This permits greater interactivity and customization, and often
looks better, due to cross-browser canvas text inconsistencies and limitations.

It can also be desirable to render the plot entirely in canvas, particularly
if the goal is to save it as an image, or if Flot is being used in a context
where the HTML DOM does not exist, as is the case within Node.js. This plugin
switches out Flot's standard drawing operations for canvas-only replacements.

Currently the plugin supports only axis labels, but it will eventually allow
every element of the plot to be rendered directly to canvas.

The plugin supports these options:

{
    canvas: boolean
}

The "canvas" option controls whether full canvas drawing is enabled, making it
possible to toggle on and off. This is useful when a plot uses HTML text in the
browser, but needs to redraw with canvas text when exporting as an image.

*/

(function($) {

	var options = {
		canvas: true
	};

	var render, getTextInfo, addText;

	// Cache the prototype hasOwnProperty for faster access

	var hasOwnProperty = Object.prototype.hasOwnProperty;

	function init(plot, classes) {

		var Canvas = classes.Canvas;

		// We only want to replace the functions once; the second time around
		// we would just get our new function back.  This whole replacing of
		// prototype functions is a disaster, and needs to be changed ASAP.

		if (render == null) {
			getTextInfo = Canvas.prototype.getTextInfo,
			addText = Canvas.prototype.addText,
			render = Canvas.prototype.render;
		}

		// Finishes rendering the canvas, including overlaid text

		Canvas.prototype.render = function() {

			if (!plot.getOptions().canvas) {
				return render.call(this);
			}

			var context = this.context,
				cache = this._textCache;

			// For each text layer, render elements marked as active

			context.save();

			// TODO: Comments in Ole's implementation indicate that some
			// browsers differ in their interpretation of 'top'; so far I
			// don't see this, but it requires more testing.  We'll stick
			// with top until this can be verified.

			// Original comment was:
			// Top alignment would be more natural, but browsers can differ a
			// pixel or two in where they consider the top to be, so instead
			// we middle align to minimize variation between browsers and
			// compensate when calculating the coordinates.

			context.textBaseline = "top";

			for (var layerKey in cache) {
				if (hasOwnProperty.call(cache, layerKey)) {
					var layerCache = cache[layerKey];
					for (var styleKey in layerCache) {
						if (hasOwnProperty.call(layerCache, styleKey)) {
							var styleCache = layerCache[styleKey],
								updateStyles = true;
							for (var key in styleCache) {
								if (hasOwnProperty.call(styleCache, key)) {

									var info = styleCache[key];

									if (!info.active) {
										delete styleCache[key];
										continue;
									}

									var x = info.x,
										y = info.y,
										lines = info.lines,
										halign = info.halign;

									// Since every element at this level of the cache have the
									// same font and fill styles, we can just change them once
									// using the values from the first element.

									if (updateStyles) {
										context.fillStyle = info.font.color;
										context.font = info.font.definition;
										updateStyles = false;
									}

									for (var i = 0; i < lines.length; ++i) {

										var line = lines[i],
											linex = x;

										// Apply horizontal alignment per-line

										if (halign == "center") {
											linex -= line.width / 2;
										} else if (halign == "right") {
											linex -= line.width;
										}

										// FIXME: LEGACY BROWSER FIX
										// AFFECTS: Opera < 12.00

										// Round the coordinates, since Opera otherwise
										// switches to uglier (probably non-hinted) rendering.
										// Also offset the y coordinate, since Opera is off
										// pretty consistently compared to the other browsers.

										if (!!(window.opera && window.opera.version().split(".")[0] < 12)) {
											linex = Math.floor(linex);
											y = Math.ceil(y - 2);
										}

										context.fillText(line.text, Math.round(linex), Math.round(y));
										y += line.height;
									}
								}
							}
						}
					}
				}
			}

			context.restore();
		};

		// Creates (if necessary) and returns a text info object.
		//
		// When the canvas option is set, the object looks like this:
		//
		// {
		//     x: X coordinate at which the text is located.
		//     x: Y coordinate at which the text is located.
		//     width: Width of the text's bounding box.
		//     height: Height of the text's bounding box.
		//     active: Flag indicating whether the text should be visible.
		//     lines: [{
		//         height: Height of this line.
		//         widths: Width of this line.
		//         text: Text on this line.
		//     }],
		//     font: {
		//         definition: Canvas font property string.
		//         color: Color of the text.
		//     },
		// }

		Canvas.prototype.getTextInfo = function(layer, text, font, angle) {

			if (!plot.getOptions().canvas) {
				return getTextInfo.call(this, layer, text, font, angle);
			}

			var textStyle, layerCache, styleCache, info;

			// Cast the value to a string, in case we were given a number

			text = "" + text;

			// If the font is a font-spec object, generate a CSS definition

			if (typeof font === "object") {
				textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;
			} else {
				textStyle = font;
			}

			// Retrieve (or create) the cache for the text's layer and styles

			layerCache = this._textCache[layer];

			if (layerCache == null) {
				layerCache = this._textCache[layer] = {};
			}

			styleCache = layerCache[textStyle];

			if (styleCache == null) {
				styleCache = layerCache[textStyle] = {};
			}

			info = styleCache[text];

			if (info == null) {

				var context = this.context;

				// If the font was provided as CSS, create a div with those
				// classes and examine it to generate a canvas font spec.

				// Note the trick of using a line-height of 1, without units;
				// this sets it equal to the font-size, even if the font-size
				// is something abstract, like 'smaller'.  This enables us to
				// read the real font-size via the element's height, working
				// around browsers that return the literal 'smaller' value.

				if (typeof font !== "object") {

					var element = $("

")
						.addClass(typeof font === "string" ? font : null)
						.css({ position: "absolute", padding: 0, 'line-height': 1 })
						.appendTo(this.getTextLayer(layer));

					font = {
						style: element.css("font-style"),
						variant: element.css("font-variant"),
						weight: element.css("font-weight"),
						size: element.height(),
						family: element.css("font-family"),
						color: element.css("color")
					};

					element.remove();
				}

				textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;

				// Create a new info object, initializing the dimensions to
				// zero so we can count them up line-by-line.

				info = styleCache[text] = {
					x: null,
					y: null,
					width: 0,
					height: 0,
					active: false,
					lines: [],
					font: {
						definition: textStyle,
						color: font.color
					}
				};

				context.save();
				context.font = textStyle;

				// Canvas can't handle multi-line strings; break on various
				// newlines, including HTML brs, to build a list of lines.
				// Note that we could split directly on regexps, but IE < 9 is
				// broken; revisit when we drop IE 7/8 support.

				var lines = (text + "").replace(/
|\r\n|\r/g, "\n").split("\n");

				for (var i = 0; i < lines.length; ++i) {

					var lineText = lines[i],
						measured = context.measureText(lineText),
						lineWidth, lineHeight;

					lineWidth = measured.width;

					// Height might not be defined; not in the standard yet

					lineHeight = measured.height || font.size;

					// Add a bit of margin since font rendering is not pixel
					// perfect and cut off letters look bad.  This also doubles
					// as spacing between lines.

					lineHeight += Math.round(font.size * 0.15);

					info.width = Math.max(lineWidth, info.width);
					info.height += lineHeight;

					info.lines.push({
						text: lineText,
						width: lineWidth,
						height: lineHeight
					});
				}

				context.restore();
			}

			return info;
		};

		// Adds a text string to the canvas text overlay.

		Canvas.prototype.addText = function(layer, x, y, text, font, angle, halign, valign) {

			if (!plot.getOptions().canvas) {
				return addText.call(this, layer, x, y, text, font, angle, halign, valign);
			}

			var info = this.getTextInfo(layer, text, font, angle);

			// Mark the text for inclusion in the next render pass

			info.active = true;

			// Save horizontal alignment for later; we'll apply it per-line

			info.x = x;
			info.halign = halign;

			// Tweak the initial y-position to match vertical alignment

			if (valign == "middle") {
				info.y = y - info.height / 2;
			} else if (valign == "bottom") {
				info.y = y - info.height;
			} else {
				info.y = y;
			}
		};
	}

	$.plot.plugins.push({
		init: init,
		options: options,
		name: "canvas",
		version: "1.0"
	});

})(jQuery);
<|endoftext|>


<commit_before>var i;
var sampleList = [];
for (i = 1; i <= 21; i++)
  sampleList.push(new ToolBarItem("Sample " + i, "sample" + i, undefined, ToolTypeEnum.BUTTON, undefined, undefined));

function ToolBarViewModel() {
  var self = this;
  /**
   * All ToolBarGroups and Items are created here.
   * Knockout.js will automatically draw these and
   * maintain the events ocurred and automatically
   * update the value here.
   */
  self.toolbarGroups = [
    new ToolBarGroup("File: ", [
      new ToolBarItem("Undo", "undo", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Redo", "redo", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Reset", "reset", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Save", "save", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Open", "open", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Export", "export_svg", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      self.samples = new ToolBarItem("Samples", "samples", 4, ToolTypeEnum.RADIOLIST, sampleList)
    ]),
    self.tools = new ToolBarGroup("Tools: ", [
      new ToolBarItem("Ray", "tool_laser", "ray",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Beam", "tool_parallel", "beam",
        ToolTypeEnum.RADIO),
      self.point_sources = new ToolBarItem("Point Source", "tool_radiant_", 6, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("360 degrees", "tool_radiant", "point_source",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Finite angle", "tool_led", "led",
          ToolTypeEnum.RADIO)
      ]),
      self.blockers = new ToolBarItem("Blockers", "tool_blocker_", undefined, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Line Blocker", "tool_blackline", "blocker",
            ToolTypeEnum.RADIO),
        new ToolBarItem("Circle Blocker", "tool_blackcircle", "circle_blocker",
            ToolTypeEnum.RADIO)
      ]),
      self.mirrors = new ToolBarItem("Mirrors", "tool_mirror_", 4, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Segment", "tool_mirror", "mirror",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Circular Arc", "tool_arcmirror", "mirror_arc",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Ideal Curved", "tool_idealmirror", "ideal_curved_mirror",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Parabolic", "tool_parabolicmirror", "mirror_parabolic",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Beam Splitter", "tool_beamsplitter", "beamsplitter",
          ToolTypeEnum.RADIO)
      ]),
      self.glasses = new ToolBarItem("Glasses", "tool_refractor_", 3, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Half-plane", "tool_halfplane", "glass_halfplane",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Circle", "tool_circlelens", "glass_circle",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Free-shape", "tool_refractor", "glass",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Ideal Lens", "tool_lens", "ideal_lens",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Spherical Lens", "tool_sphericallens", "spherical_lens",
          ToolTypeEnum.RADIO)
      ]),
      new ToolBarItem("Ruler", "tool_ruler", "ruler",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Protractor", "tool_protractor", "protractor",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Power measurement", "tool_power", "power",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Text", "tool_text", undefined,
        ToolTypeEnum.RADIO),
      new ToolBarItem("Move View", "tool_", undefined,
        ToolTypeEnum.RADIO)
    ]),
    self.modes = new ToolBarGroup("View: ", [
      new ToolBarItem("Rays", "mode_light", "normal",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Extended Rays", "mode_extended_light", "extended_rays",
        ToolTypeEnum.RADIO),
      new ToolBarItem("All Images", "mode_images", "all_images",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Seen by Observer", "mode_observer", "seen_by_observer",
        ToolTypeEnum.RADIO),
      self.colorMode = new ToolBarItem("Simulate Colors", "color_mode", undefined,
        ToolTypeEnum.CHECK)
    ]),
    new ToolBarGroup("Settings: ", [
      self.rayDensity = new ToolBarItem("Ray Density", "rayDensity", undefined,
        ToolTypeEnum.SLIDE, undefined, undefined,
        -3, 3, 0.0001, -2.3026),
      self.c1 = new ToolBarItem("Grid", "showgrid", undefined,
        ToolTypeEnum.CHECK),
      self.c2 = new ToolBarItem("Snap to Grid", "grid", undefined,
        ToolTypeEnum.CHECK),
      self.c3 = new ToolBarItem("Lock Objects", "lockobjs", undefined,
        ToolTypeEnum.CHECK),
      self.zoom = new ToolBarItem("Zoom", "zoom", undefined,
        ToolTypeEnum.SLIDE, undefined, undefined,
        25, 500, 25, 100),
      self.help = new ToolBarItem("Help", "help", undefined,
        ToolTypeEnum.HELP, undefined)
    ])
  ];
}

init_i18n();

var toolBarViewModel = new ToolBarViewModel();
ko.applyBindings(toolBarViewModel);

window.toolBarViewModel = toolBarViewModel;

try {
  if (localStorage.rayOpticsHelp == "off") {
    $("#help").prop('checked', false);
    $("[data-toggle=popover]").popover("disable");
    $("#help").parent().popover("enable");
    window.toolBarViewModel.help.selected(false)
  }
} catch { }
$("#help").click(function () {
  if (this.checked) {
    console.log(1);
    $("[data-toggle=popover]").popover("enable");
    localStorage.rayOpticsHelp = "on";
  } else {
    console.log(0);
    $("[data-toggle=popover]").popover("disable");
    $("#help").parent().popover("enable");
    localStorage.rayOpticsHelp = "off";
  }
});

$("#color_mode").parent().removeClass("btn-primary").addClass("btn-secondary").css("margin-left","10px");
$("#color_mode").next()[0].innerHTML += 'beta'
<commit_msg>Changed sampleList size from 21 to 23<commit_after>var i;
var sampleList = [];
for (i = 1; i <= 23; i++)
  sampleList.push(new ToolBarItem("Sample " + i, "sample" + i, undefined, ToolTypeEnum.BUTTON, undefined, undefined));

function ToolBarViewModel() {
  var self = this;
  /**
   * All ToolBarGroups and Items are created here.
   * Knockout.js will automatically draw these and
   * maintain the events ocurred and automatically
   * update the value here.
   */
  self.toolbarGroups = [
    new ToolBarGroup("File: ", [
      new ToolBarItem("Undo", "undo", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Redo", "redo", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Reset", "reset", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Save", "save", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Open", "open", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      new ToolBarItem("Export", "export_svg", undefined,
        ToolTypeEnum.BUTTON, undefined, function () { alert(""); }),
      self.samples = new ToolBarItem("Samples", "samples", 4, ToolTypeEnum.RADIOLIST, sampleList)
    ]),
    self.tools = new ToolBarGroup("Tools: ", [
      new ToolBarItem("Ray", "tool_laser", "ray",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Beam", "tool_parallel", "beam",
        ToolTypeEnum.RADIO),
      self.point_sources = new ToolBarItem("Point Source", "tool_radiant_", 6, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("360 degrees", "tool_radiant", "point_source",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Finite angle", "tool_led", "led",
          ToolTypeEnum.RADIO)
      ]),
      self.blockers = new ToolBarItem("Blockers", "tool_blocker_", undefined, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Line Blocker", "tool_blackline", "blocker",
            ToolTypeEnum.RADIO),
        new ToolBarItem("Circle Blocker", "tool_blackcircle", "circle_blocker",
            ToolTypeEnum.RADIO)
      ]),
      self.mirrors = new ToolBarItem("Mirrors", "tool_mirror_", 4, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Segment", "tool_mirror", "mirror",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Circular Arc", "tool_arcmirror", "mirror_arc",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Ideal Curved", "tool_idealmirror", "ideal_curved_mirror",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Parabolic", "tool_parabolicmirror", "mirror_parabolic",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Beam Splitter", "tool_beamsplitter", "beamsplitter",
          ToolTypeEnum.RADIO)
      ]),
      self.glasses = new ToolBarItem("Glasses", "tool_refractor_", 3, ToolTypeEnum.RADIOLIST, [
        new ToolBarItem("Half-plane", "tool_halfplane", "glass_halfplane",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Circle", "tool_circlelens", "glass_circle",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Free-shape", "tool_refractor", "glass",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Ideal Lens", "tool_lens", "ideal_lens",
          ToolTypeEnum.RADIO),
        new ToolBarItem("Spherical Lens", "tool_sphericallens", "spherical_lens",
          ToolTypeEnum.RADIO)
      ]),
      new ToolBarItem("Ruler", "tool_ruler", "ruler",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Protractor", "tool_protractor", "protractor",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Power measurement", "tool_power", "power",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Text", "tool_text", undefined,
        ToolTypeEnum.RADIO),
      new ToolBarItem("Move View", "tool_", undefined,
        ToolTypeEnum.RADIO)
    ]),
    self.modes = new ToolBarGroup("View: ", [
      new ToolBarItem("Rays", "mode_light", "normal",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Extended Rays", "mode_extended_light", "extended_rays",
        ToolTypeEnum.RADIO),
      new ToolBarItem("All Images", "mode_images", "all_images",
        ToolTypeEnum.RADIO),
      new ToolBarItem("Seen by Observer", "mode_observer", "seen_by_observer",
        ToolTypeEnum.RADIO),
      self.colorMode = new ToolBarItem("Simulate Colors", "color_mode", undefined,
        ToolTypeEnum.CHECK)
    ]),
    new ToolBarGroup("Settings: ", [
      self.rayDensity = new ToolBarItem("Ray Density", "rayDensity", undefined,
        ToolTypeEnum.SLIDE, undefined, undefined,
        -3, 3, 0.0001, -2.3026),
      self.c1 = new ToolBarItem("Grid", "showgrid", undefined,
        ToolTypeEnum.CHECK),
      self.c2 = new ToolBarItem("Snap to Grid", "grid", undefined,
        ToolTypeEnum.CHECK),
      self.c3 = new ToolBarItem("Lock Objects", "lockobjs", undefined,
        ToolTypeEnum.CHECK),
      self.zoom = new ToolBarItem("Zoom", "zoom", undefined,
        ToolTypeEnum.SLIDE, undefined, undefined,
        25, 500, 25, 100),
      self.help = new ToolBarItem("Help", "help", undefined,
        ToolTypeEnum.HELP, undefined)
    ])
  ];
}

init_i18n();

var toolBarViewModel = new ToolBarViewModel();
ko.applyBindings(toolBarViewModel);

window.toolBarViewModel = toolBarViewModel;

try {
  if (localStorage.rayOpticsHelp == "off") {
    $("#help").prop('checked', false);
    $("[data-toggle=popover]").popover("disable");
    $("#help").parent().popover("enable");
    window.toolBarViewModel.help.selected(false)
  }
} catch { }
$("#help").click(function () {
  if (this.checked) {
    console.log(1);
    $("[data-toggle=popover]").popover("enable");
    localStorage.rayOpticsHelp = "on";
  } else {
    console.log(0);
    $("[data-toggle=popover]").popover("disable");
    $("#help").parent().popover("enable");
    localStorage.rayOpticsHelp = "off";
  }
});

$("#color_mode").parent().removeClass("btn-primary").addClass("btn-secondary").css("margin-left","10px");
$("#color_mode").next()[0].innerHTML += 'beta'
<|endoftext|>