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

const minimum_character_length = 25;

// Variable to store user preferences
let userPreferences,
// A div that shows feedback of detected language:
    feedbackDiv = document.createElement("div"),
    // In this variable we keep a reference to the input element which the addon operates on currently
    currentInputElement,
    ignoreSignature;

// Initialize the feedback div. It has our unique id
feedbackDiv.id = "danielnaber-firefox-dict-switcher-tooltip";

// Listen to a message from the main script containing user preferences
self.port.on("config", function (prefs)
{
    // We require the presence of user preferences just to know the languages for which the user chosen "Don't
    // detect this language" option so that we disable spell checking if we encountered text in these languages
    userPreferences = prefs;
    ignoreSignature = userPreferences["ignoreSignature"];
});

// The main script sends this message after it receives the detected language code, and found the language dialect
// (ie. the dictionary) that will be used, or found that there's no dictionary for the detected language
// See the changeDictionary function in main.js
self.port.on("feedback", function (feedback)
{
    showFeedback(currentInputElement, feedback.language, feedback.message, feedback.isWarning);
});

function detectAndSetLanguage(targetElement, text)
{
    // If spell checking was disabled by the page developer, we honor the setting and don't set the value of
    // the spellcheck attribute to true, but we still try to detect the language and keep the ordinary operation
    // of the addon because the user can manually enable spell checking from the context menu, and the spellcheck
    // attribute stays false.
    // I set the developerDisabledSpellChecking to indicate this case
    // (see how the firefoxDictSwitcherDisabledSpellCheck flag is set later)
    // I use === because I'm not sure if this attribute accepts only booleans
    const developerDisabledSpellChecking = targetElement.spellcheck === false &&
                                          !targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck;

    // text is an optional parameter used when handling the paste event only. Otherwise, we read the text from
    // the element itself
    text = text || targetElement.value || targetElement.textContent;

    if(ignoreSignature)
    {
        let signatureDelimiterPos = text.indexOf("-- \n");

        if(signatureDelimiterPos >= 0)
        {
            // cut off signature: it may be written in a different language than
            // the main text and would thus decrease language detection quality:
            text = text.substring(0, signatureDelimiterPos);
        }
    }

    // we are only going to check language if there is some amount of text available as
    // that will increase our chances of detecting language correctly.
    if(text.length > minimum_character_length)
    {
        //const startTime = performance.now();

        // Looks like we have enough text to reliably detect the language.
        let detectableLanguages = ['cmn', 'spa', 'eng', 'rus', 'arb', 'ben', 'hin', 'por', 'ind', 'jpn',
                                   'fra', 'deu', 'jav', 'kor', 'tel', 'vie', 'mar', 'ita', 'tam', 'tur'];
        for (var i = 1; i <= 3; i++)
        {
            let additionalLanguage = userPreferences["additionalLanguage"+i];
            if (additionalLanguage && additionalLanguage !== '-') {
                detectableLanguages.push(additionalLanguage);
            }
        }
        /*let potentiallyDisabledLanguages = ['en', 'es', 'de', 'pt'];  // these have variants and a "Don't detect this language" option
        for(let idx in potentiallyDisabledLanguages)
        {
            let shortCode = potentiallyDisabledLanguages[idx];
            console.log("shortCode " + shortCode);
            console.log("userPreferences[" + shortCode + "] " + userPreferences[shortCode]);
            if(userPreferences[potentiallyDisabledLanguages[idx]])
            {
                let longCode = iso6393[shortCode]; TODO: this is not correct, the mapping works the other way round
                if(!longCode)
                {
                    showFeedback(targetElement, "??", "Error: Could not get long code for " + shortCode, true);
                    return;
                }
                let pos = detectableLanguages.indexOf(longCode);
                if(pos > -1)
                {
                    detectableLanguages.splice(pos, 1);
                    console.log("remove " + idx);
                }
            }
        }
        console.log("detectableLanguages " + detectableLanguages);
        */

        var language;
        try {
            language = franc(text,
                {
                    // top 20 + user settings:
                    'whitelist': detectableLanguages
                });
        } catch (e) {
            showFeedback(targetElement, "??", "Error: Could not detect language: " + e.toString(), true);
            return;
        }

        var shortCode;
        if(language === "und")  // 'unknown'
        {
            showFeedback(targetElement, "...", "Language not configured or need more characters to detect language");
            return;
        }
        else
        {
            var languageInfo = iso6393[language];
            if(!languageInfo)
            {
                showFeedback(targetElement, "??", "Error: Could not find short language code for '" + language + "'", true);
                return;
            }
            shortCode = languageInfo.iso6391;
        }

        //console.log(`Detected language code is (${language}) in ${performance.now() - startTime} ms`);

        // If the language wasn't successfully identified
        // If the user chose to ignore the detected language (see the configs in package.json)
        if(userPreferences[shortCode] == "-")
            showFeedback(targetElement, "--", "Detected " + shortCode + " but it's disabled in configuration");
        else
        {
            // We won't show the feedback tooltip until the main script sends us the language dialect that
            // will be used, and whether or not it found a dictionary for this dialect, so, keep a reference
            // for the input element the user currently edits so that we can show the feedback tooltip on it
            // later.
            // Notice that we are either passed targetElement as a parameter or use the current activeElement
            // (in case of paste events only)
            currentInputElement = targetElement || document.activeElement;

            // If the language was detected successfully enable spell checking and send its code to the main script
            if(!developerDisabledSpellChecking)
            {
                // We enable spell checking only if the attribute wasn't already set to false be the page developer
                targetElement.spellcheck = true;
            }

            self.port.emit("changeDictionary", shortCode);

            return;
        }

        // Disable spell checking...
        targetElement.spellcheck = false;

        // ...send null to indicate to the main script that either we failed to detect or the language is disabled...
        //self.port.emit("changeDictionary", null);

        // ...and set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking)
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
    }
    else
    {
        showFeedback(targetElement, "...", "Need more characters to detect language");

        // Because we can't detect the language, disable spell checking
        targetElement.spellcheck = false;

        // Sending null to the main script also indicates that the input text is too short to detect the language
        self.port.emit("changeDictionary", null);

        // And set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking)
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
    }
}

function showFeedback(element, feedbackText, feedbackTitle, isWarning)
{
    // Detach the feedback div from the document if it were previously attached
    if(feedbackDiv.parentElement)
        feedbackDiv.parentElement.removeChild(feedbackDiv);

    const parentElement = element.offsetParent;

    // offsetParent is null if the element itself is hidden. If the element is hidden we exit as, of course,
    // we won't show feedback on a hidden element
    if(!parentElement)
        return;

    currentInputElement = element;

    feedbackDiv.title = feedbackTitle;
    feedbackDiv.textContent = feedbackText;

    if(isWarning)
        feedbackDiv.classList.add("firefox-dict-switcher-warning");
    else
        feedbackDiv.classList.remove("firefox-dict-switcher-warning");

    parentElement.appendChild(feedbackDiv);

    // Initialize the positioning loop
    requestAnimationFrame(positionFeedbackDiv);
}

// This function keeps the feedback div always positioned properly relative to
function positionFeedbackDiv()
{
    const parentElement = feedbackDiv.parentElement;

    if(!parentElement)
        return;

    const leftPos = currentInputElement.offsetLeft + currentInputElement.offsetWidth - feedbackDiv.offsetWidth - 18,
          topPos = currentInputElement.offsetTop + currentInputElement.offsetHeight - feedbackDiv.offsetHeight - 5;

    feedbackDiv.style.left = leftPos + "px";
    feedbackDiv.style.top = topPos + "px";

    // Keep the loop going
    requestAnimationFrame(positionFeedbackDiv);
}

function isEligible(element)
{
    // The addon operates only on textarea elements or elements with the contentEditable attribute set
    return element.tagName == "TEXTAREA" ||
           element.contentEditable === 'true';
}

// Note from Ashraf: I commented the old code that initially iterates over all TEXTAREA elements and instead
// used event delegation by attaching event handlers only to the root element. This is better for performance
// and covers the elements that are later appended to the DOM
document.documentElement.addEventListener("keydown", function (evt)
{
    if(isEligible(evt.target))
    {
        var key = evt.keyCode || evt.charCode;

        // check if user has finished entering a word
        if(key == 32/*space*/ || key == 188/*comma*/ || key == 190/*dot*/)
        {
            detectAndSetLanguage(evt.target);
        }
    }
});

// HACK: The focus event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusin event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusin
document.documentElement.addEventListener("focus", function (evt)
{
    // Set correct language when we set the focus to already filled textarea
    if(isEligible(evt.target))
        detectAndSetLanguage(evt.target);
}, true);

// HACK: The blur event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusout event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusout
document.documentElement.addEventListener("blur", function (evt)
{
    // When the focus gets out of an element, detach the feedback div from the document
    if(feedbackDiv.parentElement)
        feedbackDiv.parentElement.removeChild(feedbackDiv);
}, true);

document.documentElement.addEventListener("paste", function (e)
{
    // We act only when data is pasted on a TEXTAREA element
    if(isEligible(e.target))
    {
        // We try to detect only if the pasted data is available in plain text format
        const text = e.clipboardData.getData("text/plain");

        if(text)
            detectAndSetLanguage(e.target, text);
    }
});<commit_msg>small cleanup<commit_after>//   Automatic Dictionary Switcher Add-on for Firefox
//   Copyright 2015 Daniel Naber
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

const minimum_character_length = 25;

// Variable to store user preferences
let userPreferences,
// A div that shows feedback of detected language:
    feedbackDiv = document.createElement("div"),
    // In this variable we keep a reference to the input element which the addon operates on currently
    currentInputElement,
    ignoreSignature;

// Initialize the feedback div. It has our unique id
feedbackDiv.id = "danielnaber-firefox-dict-switcher-tooltip";

// Listen to a message from the main script containing user preferences
self.port.on("config", function (prefs)
{
    // We require the presence of user preferences just to know the languages for which the user chosen "Don't
    // detect this language" option so that we disable spell checking if we encountered text in these languages
    userPreferences = prefs;
    ignoreSignature = userPreferences["ignoreSignature"];
});

// The main script sends this message after it receives the detected language code, and found the language dialect
// (ie. the dictionary) that will be used, or found that there's no dictionary for the detected language
// See the changeDictionary function in main.js
self.port.on("feedback", function (feedback)
{
    showFeedback(currentInputElement, feedback.language, feedback.message, feedback.isWarning);
});

function detectAndSetLanguage(targetElement, text)
{
    // If spell checking was disabled by the page developer, we honor the setting and don't set the value of
    // the spellcheck attribute to true, but we still try to detect the language and keep the ordinary operation
    // of the addon because the user can manually enable spell checking from the context menu, and the spellcheck
    // attribute stays false.
    // I set the developerDisabledSpellChecking to indicate this case
    // (see how the firefoxDictSwitcherDisabledSpellCheck flag is set later)
    // I use === because I'm not sure if this attribute accepts only booleans
    const developerDisabledSpellChecking = targetElement.spellcheck === false &&
                                          !targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck;

    // text is an optional parameter used when handling the paste event only. Otherwise, we read the text from
    // the element itself
    text = text || targetElement.value || targetElement.textContent;

    if(ignoreSignature)
    {
        let signatureDelimiterPos = text.indexOf("-- \n");

        if(signatureDelimiterPos >= 0)
        {
            // cut off signature: it may be written in a different language than
            // the main text and would thus decrease language detection quality:
            text = text.substring(0, signatureDelimiterPos);
        }
    }

    // we are only going to check language if there is some amount of text available as
    // that will increase our chances of detecting language correctly.
    if(text.length > minimum_character_length)
    {
        //const startTime = performance.now();

        let detectableLanguages = getDetectableLanguages();

        var language;
        try {
            language = franc(text,
                {
                    // top 20 + user settings:
                    'whitelist': detectableLanguages
                });
        } catch (e) {
            showFeedback(targetElement, "??", "Error: Could not detect language: " + e.toString(), true);
            return;
        }

        var shortCode;
        if(language === "und")  // 'unknown'
        {
            showFeedback(targetElement, "...", "Language not configured or need more characters to detect language");
            return;
        }
        else
        {
            var languageInfo = iso6393[language];
            if(!languageInfo)
            {
                showFeedback(targetElement, "??", "Error: Could not find short language code for '" + language + "'", true);
                return;
            }
            shortCode = languageInfo.iso6391;
        }

        //console.log(`Detected language code is (${language}) in ${performance.now() - startTime} ms`);

        // If the language wasn't successfully identified
        // If the user chose to ignore the detected language (see the configs in package.json)
        if(userPreferences[shortCode] == "-")
            showFeedback(targetElement, "--", "Detected " + shortCode + " but it's disabled in configuration");
        else
        {
            // We won't show the feedback tooltip until the main script sends us the language dialect that
            // will be used, and whether or not it found a dictionary for this dialect, so, keep a reference
            // for the input element the user currently edits so that we can show the feedback tooltip on it
            // later.
            // Notice that we are either passed targetElement as a parameter or use the current activeElement
            // (in case of paste events only)
            currentInputElement = targetElement || document.activeElement;

            // If the language was detected successfully enable spell checking and send its code to the main script
            if(!developerDisabledSpellChecking)
            {
                // We enable spell checking only if the attribute wasn't already set to false be the page developer
                targetElement.spellcheck = true;
            }

            self.port.emit("changeDictionary", shortCode);

            return;
        }

        // Disable spell checking...
        targetElement.spellcheck = false;

        // ...send null to indicate to the main script that either we failed to detect or the language is disabled...
        //self.port.emit("changeDictionary", null);

        // ...and set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking)
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
    }
    else
    {
        showFeedback(targetElement, "...", "Need more characters to detect language");

        // Because we can't detect the language, disable spell checking
        targetElement.spellcheck = false;

        // Sending null to the main script also indicates that the input text is too short to detect the language
        self.port.emit("changeDictionary", null);

        // And set a flag to indicate that it's our code who disabled spell checking not the page developer's
        // We do so only if the attribute wasn't already set to false be the page developer
        if(!developerDisabledSpellChecking)
            targetElement.dataset.firefoxDictSwitcherDisabledSpellCheck = "1";
    }
}

function getDetectableLanguages()
{
    // Looks like we have enough text to reliably detect the language.
    let detectableLanguages = ['cmn', 'spa', 'eng', 'rus', 'arb', 'ben', 'hin', 'por', 'ind', 'jpn',
                               'fra', 'deu', 'jav', 'kor', 'tel', 'vie', 'mar', 'ita', 'tam', 'tur'];
    for (var i = 1; i <= 3; i++)
    {
        let additionalLanguage = userPreferences["additionalLanguage"+i];
        if (additionalLanguage && additionalLanguage !== '-') {
            detectableLanguages.push(additionalLanguage);
        }
    }
    /*let potentiallyDisabledLanguages = ['en', 'es', 'de', 'pt'];  // these have variants and a "Don't detect this language" option
    for (let idx in potentiallyDisabledLanguages)
    {
        let shortCode = potentiallyDisabledLanguages[idx];
        console.log("shortCode " + shortCode);
        console.log("userPreferences[" + shortCode + "] " + userPreferences[shortCode]);
        if (userPreferences[potentiallyDisabledLanguages[idx]])
        {
            let longCode = iso6393[shortCode];
            // TODO: this is not correct, the mapping works the other way round
            if (!longCode)
            {
                showFeedback(targetElement, "??", "Error: Could not get long code for " + shortCode, true);
                return;
            }
            let pos = detectableLanguages.indexOf(longCode);
            if (pos > -1)
            {
                detectableLanguages.splice(pos, 1);
                console.log("remove " + idx);
            }
        }
    }
    console.log("detectableLanguages " + detectableLanguages);
    */
    return detectableLanguages;
}

function showFeedback(element, feedbackText, feedbackTitle, isWarning)
{
    // Detach the feedback div from the document if it were previously attached
    if(feedbackDiv.parentElement)
        feedbackDiv.parentElement.removeChild(feedbackDiv);

    const parentElement = element.offsetParent;

    // offsetParent is null if the element itself is hidden. If the element is hidden we exit as, of course,
    // we won't show feedback on a hidden element
    if(!parentElement)
        return;

    currentInputElement = element;

    feedbackDiv.title = feedbackTitle;
    feedbackDiv.textContent = feedbackText;

    if(isWarning)
        feedbackDiv.classList.add("firefox-dict-switcher-warning");
    else
        feedbackDiv.classList.remove("firefox-dict-switcher-warning");

    parentElement.appendChild(feedbackDiv);

    // Initialize the positioning loop
    requestAnimationFrame(positionFeedbackDiv);
}

// This function keeps the feedback div always positioned properly relative to
function positionFeedbackDiv()
{
    const parentElement = feedbackDiv.parentElement;

    if(!parentElement)
        return;

    const leftPos = currentInputElement.offsetLeft + currentInputElement.offsetWidth - feedbackDiv.offsetWidth - 18,
          topPos = currentInputElement.offsetTop + currentInputElement.offsetHeight - feedbackDiv.offsetHeight - 5;

    feedbackDiv.style.left = leftPos + "px";
    feedbackDiv.style.top = topPos + "px";

    // Keep the loop going
    requestAnimationFrame(positionFeedbackDiv);
}

function isEligible(element)
{
    // The addon operates only on textarea elements or elements with the contentEditable attribute set
    return element.tagName == "TEXTAREA" ||
           element.contentEditable === 'true';
}

// Note from Ashraf: I commented the old code that initially iterates over all TEXTAREA elements and instead
// used event delegation by attaching event handlers only to the root element. This is better for performance
// and covers the elements that are later appended to the DOM
document.documentElement.addEventListener("keydown", function (evt)
{
    if(isEligible(evt.target))
    {
        var key = evt.keyCode || evt.charCode;

        // check if user has finished entering a word
        if(key == 32/*space*/ || key == 188/*comma*/ || key == 190/*dot*/)
        {
            detectAndSetLanguage(evt.target);
        }
    }
});

// HACK: The focus event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusin event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusin
document.documentElement.addEventListener("focus", function (evt)
{
    // Set correct language when we set the focus to already filled textarea
    if(isEligible(evt.target))
        detectAndSetLanguage(evt.target);
}, true);

// HACK: The blur event doesn't bubble, so I set the useCapture parameter of addEventListener to true.
// We should use the focusout event which bubbles but it's not supported on Firefox as of the date I wrote this.
// See the note on this article https://developer.mozilla.org/en-US/docs/Web/Events/focusout
document.documentElement.addEventListener("blur", function (evt)
{
    // When the focus gets out of an element, detach the feedback div from the document
    if(feedbackDiv.parentElement)
        feedbackDiv.parentElement.removeChild(feedbackDiv);
}, true);

document.documentElement.addEventListener("paste", function (e)
{
    // We act only when data is pasted on a TEXTAREA element
    if(isEligible(e.target))
    {
        // We try to detect only if the pasted data is available in plain text format
        const text = e.clipboardData.getData("text/plain");

        if(text)
            detectAndSetLanguage(e.target, text);
    }
});<|endoftext|>


<commit_before>$(document).ready(function() {

    $('#id_email,#footer_email_submit,#id_interest').focus(function () {
        $('#form-details').slideDown();
        $('#footer-email-form .form-details').slideDown();
    });

});
<commit_msg>refactored footer-email-form.js to delay form validation until privacy checkbox is visible<commit_after>$(document).ready(function() {
	function footer_email_form_show_details(trigger_click) {
		$('#form-details').slideDown('normal', function() {
			if (trigger_click) {
				$('#footer_email_submit').trigger('click');
			}
        });

        $('#footer-email-form .form-details').slideDown();
	}

    $('#id_email, #id_interest').focus(function () {
        footer_email_form_show_details(false);
    });

    $('#footer_email_submit').click(function(e) {
		if (!$('#form-details').is(':visible')) {
			e.preventDefault();
			footer_email_form_show_details(true);
		}
    });

});
<|endoftext|>


<commit_before>/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (c) 2014 Mozilla Corporation
 */
import { Template } from 'meteor/templating';
import '/imports/collections.js';
import '/imports/settings.js';
import '/imports/helpers.js';
import '/client/router.js';
import '/client/mozdef.js';
import crossfilter from 'crossfilter2';
import dc from 'dc';
import 'dc/dc.css';
import { tooltip } from 'meteor/twbs:bootstrap';

if (Meteor.isClient) {

    //elastic search cluster template functions
    //return es health items
    Template.mozdefhealth.helpers({

        esclusterhealthitems: function () {
            return healthescluster.find();
        },
        frontendhealthitems: function () {
            return healthfrontend.find({},
                                   {fields:{},
                                    sort: {hostname: 1}
                                   });
        },
        sqsstatsitems: function () {
            return sqsstats.find({},
                             {fields:{},
                              sort: {hostname: 1}
                             });
        },
        esnodeshealthitems: function () {
            return healthesnodes.find({},
                                  {fields:{},
                                  sort: {hostname: 1}
                                  });
        },

        eshotthreadshealthitems: function () {
            return healtheshotthreads.find();
        }
    });

   Template.mozdefhealth.rendered = function () {
        var ringChartEPS   = dc.pieChart("#ringChart-EPS");
        var totalEPS   = dc.numberDisplay("#total-EPS");
        var ringChartLoadAverage = dc.pieChart("#ringChart-LoadAverage");

        refreshChartData=function(){
            var frontEndData=healthfrontend.find({}).fetch();
            var ndx = crossfilter(frontEndData);

            if ( frontEndData.length === 0 && ndx.size()>0){
                debugLog('clearing ndx/dc.js');
                dc.filterAll();
                ndx.remove();
                dc.redrawAll();
            } else {
                ndx = crossfilter(frontEndData);
            }
            if ( ndx.size() >0){
                var hostDim  = ndx.dimension(function(d) {return d.hostname;});
                var hostEPS = hostDim.group().reduceSum(function(d) {return d.details.total_deliver_eps.toFixed(2);});
                var hostLoadAverage = hostDim.group().reduceSum(function(d) {return d.details.loadaverage[0];});
                var epsTotal = ndx.groupAll().reduceSum(function(d) {return d.details.total_deliver_eps;});

                totalEPS
                    .valueAccessor(function(d){return d;})
                    .group(epsTotal);

                ringChartEPS
                    .width(150).height(150)
                    .dimension(hostDim)
                    .group(hostEPS)
                    .label(function(d) {return d.value ||''; })
                    .innerRadius(30)
                    .filter = function() {};

                ringChartLoadAverage
                    .width(150).height(150)
                    .dimension(hostDim)
                    .group(hostLoadAverage)
                    .label(function(d) {return d.value ||''; })
                    .innerRadius(30)
                    .filter = function() {};

                dc.renderAll();
            }
        }

        Deps.autorun(function() {
            Meteor.subscribe("healthfrontend",onReady=function(){
                refreshChartData();
            });
            Meteor.subscribe("sqsstats");
            Meteor.subscribe("healthescluster");
            Meteor.subscribe("healthesnodes");
            Meteor.subscribe("healtheshotthreads");
            //using dc.js doesn't trigger the reactive update
            //so update a UI object and refresh dc.js so both get data when it updates.
            var obj = healthfrontend.findOne();
            if (obj) {
               $('.lastupdate').text('Last Update: ' + obj.utctimestamp);
               refreshChartData();
            }
        }); //end deps.autorun

        this.$('[data-toggle="tooltip"]').tooltip({
            'placement': 'top'
        });
     };

    Template.mozdefhealth.destroyed = function () {
        dc.deregisterAllCharts();
    };
}<commit_msg>formatting<commit_after>/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (c) 2014 Mozilla Corporation
 */
import { Template } from 'meteor/templating';
import crossfilter from 'crossfilter2';
import dc from 'dc';
import 'dc/dc.css';
import { tooltip } from 'meteor/twbs:bootstrap';

if ( Meteor.isClient ) {

    //elastic search cluster template functions
    //return es health items
    Template.mozdefhealth.helpers( {

        esclusterhealthitems: function() {
            return healthescluster.find();
        },
        frontendhealthitems: function() {
            return healthfrontend.find( {},
                {
                    fields: {},
                    sort: { hostname: 1 }
                } );
        },
        sqsstatsitems: function() {
            return sqsstats.find( {},
                {
                    fields: {},
                    sort: { hostname: 1 }
                } );
        },
        esnodeshealthitems: function() {
            return healthesnodes.find( {},
                {
                    fields: {},
                    sort: { hostname: 1 }
                } );
        },

        eshotthreadshealthitems: function() {
            return healtheshotthreads.find();
        }
    } );

    Template.mozdefhealth.rendered = function() {
        var ringChartEPS = dc.pieChart( "#ringChart-EPS" );
        var totalEPS = dc.numberDisplay( "#total-EPS" );
        var ringChartLoadAverage = dc.pieChart( "#ringChart-LoadAverage" );

        refreshChartData = function() {
            var frontEndData = healthfrontend.find( {} ).fetch();
            var ndx = crossfilter( frontEndData );

            if ( frontEndData.length === 0 && ndx.size() > 0 ) {
                debugLog( 'clearing ndx/dc.js' );
                dc.filterAll();
                ndx.remove();
                dc.redrawAll();
            } else {
                ndx = crossfilter( frontEndData );
            }
            if ( ndx.size() > 0 ) {
                var hostDim = ndx.dimension( function( d ) { return d.hostname; } );
                var hostEPS = hostDim.group().reduceSum( function( d ) { return d.details.total_deliver_eps.toFixed( 2 ); } );
                var hostLoadAverage = hostDim.group().reduceSum( function( d ) { return d.details.loadaverage[0]; } );
                var epsTotal = ndx.groupAll().reduceSum( function( d ) { return d.details.total_deliver_eps; } );

                totalEPS
                    .valueAccessor( function( d ) { return d; } )
                    .group( epsTotal );

                ringChartEPS
                    .width( 150 ).height( 150 )
                    .dimension( hostDim )
                    .group( hostEPS )
                    .label( function( d ) { return d.value || ''; } )
                    .innerRadius( 30 )
                    .filter = function() { };

                ringChartLoadAverage
                    .width( 150 ).height( 150 )
                    .dimension( hostDim )
                    .group( hostLoadAverage )
                    .label( function( d ) { return d.value || ''; } )
                    .innerRadius( 30 )
                    .filter = function() { };

                dc.renderAll();
            }
        }

        Deps.autorun( function() {
            Meteor.subscribe( "healthfrontend", onReady = function() {
                refreshChartData();
            } );
            Meteor.subscribe( "sqsstats" );
            Meteor.subscribe( "healthescluster" );
            Meteor.subscribe( "healthesnodes" );
            Meteor.subscribe( "healtheshotthreads" );
            //using dc.js doesn't trigger the reactive update
            //so update a UI object and refresh dc.js so both get data when it updates.
            var obj = healthfrontend.findOne();
            if ( obj ) {
                $( '.lastupdate' ).text( 'Last Update: ' + obj.utctimestamp );
                refreshChartData();
            }
        } ); //end deps.autorun

        this.$( '[data-toggle="tooltip"]' ).tooltip( {
            'placement': 'top'
        } );
    };

    Template.mozdefhealth.destroyed = function() {
        dc.deregisterAllCharts();
    };
}<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
chai.config.includeStack = true;
var assert = chai.assert;

var path = require('path'),
    stringify = require('json-stringify-safe');

var emulator = require('../../src/emulator'),
    compiler = require('../../src/ghc/compiler');

describe('Sample tests', function () {
    it.only('should compile a program', function () {
        var code = [
            '; consts',
            'fright := 1',

            '; awareness',
            'me := who( )',
            'myStartX, myStartY := start(me)',
            'myPosX, myPosY := pos(me)',
            'targetX, targetY := pacman(1)',
            'myMode := mode(me)',
            // 'ifEqual(myMode, fright, runaway, myPos, target)',
            'tile := map(0, 0)',
            'dir := findWay(myPosX, myPosY, targetX, targetY)',
            'go(dir)',
            'done()',
            'findWay: a := 0 ; start',
            'end-findWay: a := 1'
        ].join('\n');

        var ghc = compiler(code);

        console.log('------');
        console.log(ghc);
        console.log('-------');
        console.log(ghc.split('\n').length);
    });

});
<commit_msg>run all tests<commit_after>'use strict';

var chai = require('chai');
chai.config.includeStack = true;
var assert = chai.assert;

var path = require('path'),
    stringify = require('json-stringify-safe');

var emulator = require('../../src/emulator'),
    compiler = require('../../src/ghc/compiler');

describe('Sample tests', function () {
    it('should compile a program', function () {
        var code = [
            '; consts',
            'fright := 1',

            '; awareness',
            'me := who( )',
            'myStartX, myStartY := start(me)',
            'myPosX, myPosY := pos(me)',
            'targetX, targetY := pacman(1)',
            'myMode := mode(me)',
            // 'ifEqual(myMode, fright, runaway, myPos, target)',
            'tile := map(0, 0)',
            'dir := findWay(myPosX, myPosY, targetX, targetY)',
            'go(dir)',
            'done()',
            'findWay: a := 0 ; start',
            'end-findWay: a := 1'
        ].join('\n');

        var ghc = compiler(code);

        console.log('------');
        console.log(ghc);
        console.log('-------');
        console.log(ghc.split('\n').length);
    });

});
<|endoftext|>


<commit_before>/*
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'codemirror', 'cs', {
	toolbar: 'Zdroj',
	searchCode: 'Search Source',
	autoFormat: 'Format Selection',
	commentSelectedRange: 'Comment Selection',
	uncommentSelectedRange: 'Uncomment Selection',
	autoCompleteToggle: 'Enable/Disable HTML Tag Autocomplete'
});
<commit_msg>Czech (cs) language<commit_after>/*
Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'codemirror', 'cs', {
	toolbar: 'Zdroj',
	searchCode: 'Prohledat zdroj',
	autoFormat: 'Formátovat výběr',
	commentSelectedRange: 'Zakomentovat výběr',
	uncommentSelectedRange: 'Odkomentovat výběr',
	autoCompleteToggle: 'Povolit/zakázat automatické doplňování HTML tagů'
});
<|endoftext|>


<commit_before>/**
 * TinyMCE Form
 *
 * @class HatimeriaAdmin.core.form.TinyMceForm
 * @extends Ext.form.Panel
 */
(function() {

    Ext.define('HatimeriaAdmin.core.form.TinyMceForm', {
        extend: 'Hatimeria.core.form.BaseForm',
        alias: 'widget.hatimeria-tinymce',

        /**
         * Config set
         *
         * @cfg {String} tinyConfigSet
         */
        tinyConfigSet: 'extended',

        /**
         * Initial value for field
         *
         * @cfg {String} value
         */
        value: null,

        /**
         * @cfg {String} fieldLabel Treść
         */
        fieldLabel: 'Treść',

        /**
         * @cfg {String} fieldName
         */
        fieldName: 'description',

        /**
         * @cfg {Number} tinyWidth
         */
        width: 500,

        /**
         * @cfg {Number} tinyHeight
         */
        height: 350,

        /**
         * Margin corrects width of tinyMCE
         *
         * @cfg {Number} marginOffset
         */
        marginOffset: 5,

        /**
         * @cfg {Integer} labelWidth
         */
        labelWidth: 100,

        /**
         * Max text plain length
         * @cfg {Number} maxLength
         */
        maxLength: false,

        /**
         * Allow field blank before submit
         *
         * @cfg {Boolean} allowBlank
         */
        allowBlank: true,

        /**
         * Instance of embeded editor (appears after editor created)
         * @property {TinyMCE.Editor} editor
         * @private
         */
        editor: undefined,

        /**
         * Length of plain content
         * @property {Number} numChars
         * @private
         */
        numChars: 0,

        /**
         * Id of element
         * @property {String}
         * @private
         */
        elId: undefined,

        /**
         * Base config
         *
         * @cfg {Object} tinyBaseConfig
         */
        tinyBaseConfig: {
            theme: 'advanced',
            theme_advanced_toolbar_location: 'top',
            theme_advanced_toolbar_align: 'left',
            theme_advanced_statusbar_location: 'bottom',
            file_browser_callback: 'tinymce_upload',
            theme_advanced_resizing: false,
            convert_urls: false
        },

        /**
         * Defauly config sets
         *
         * @cfg {Object} tinyConfigSets
         */
        tinyConfigSets: {
            extended: {
                plugins: 'pagebreak,style,layer,table,advhr,advimage,advlink,emotions,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras,template',
                theme_advanced_buttons1: 'bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontsizeselect',
                theme_advanced_buttons2: 'bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor',
                theme_advanced_buttons3: 'tablecontrols,|,hr,sub,sup,|,charmap,media,advhr,|,print,|,ltr,rtl,|',
                theme_advanced_buttons4: 'insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak'
            },
            basic: {
                plugins: 'advimage',
                theme_advanced_buttons1 : "formatselect,fontsizeselect,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,bullist,numlist,undo,redo,link,unlink,image",
                theme_advanced_buttons2 : "",
                theme_advanced_buttons3 : "",
                theme_advanced_buttons4 : ""
            }
        },

        statics: {
            testMe: function() {
                var panel = Ext.create("Hatimeria.core.form.BaseForm", {
                    renderTo: Ext.getBody(),
                    dockedItems: [{
                       dock: 'bottom',
                       xtype: 'toolbar',
                       items: [
                           {
                                text: 'Submit',
                                handler: function() {
                                    console.log(panel.getForm().isValid());
                                }
                           }
                       ]
                    }],
                    items: [
                        Ext.create("HatimeriaAdmin.core.form.TinyMceForm", {
                            maxLength: 2,
                            tinyConfigSet: 'basic',
                            value: 'ala123'
                        }),
                        {
                            xtype: 'numberfield',
                            fieldLabel: 'Numer',
                            name: 'test',
                            allowBlank: false
                        }
                    ],
                    height: 1000,
                    width: 800
                });

                panel.getForm().isValid();
            }
        },

        /**
         * Initialization
         */
        initComponent: function()
        {
            var _this = this;
            this.tinyBaseConfig.height = this.computeHeight();
            var config = {
                border: 0,
                layout: 'auto',
                items: [
                    {
                        itemId: 'tinymce',
                        xtype: 'tinymce',
                        width: ((this.fieldLabel && this.fieldLabel != '') ? (this.width - this.labelWidth - this.marginOffset) : this.width),
                        height: _this.height,
                        fieldLabel: this.fieldLabel,
                        labelWidth: this.labelWidth,
                        name: this.fieldName,
                        tinymceSettings: this.getTinyConfig(),
                        allowBlank: this.allowBlank,
                        listeners: {
                            change: function() {
                                _this.fireEvent('tinychange', _this, _this.getEditor())
                            }
                        },
                        validator: function() {
                            if (_this.maxLength && _this.getNumChars() > _this.maxLength)
                            {
                                return "Opis jest zbyt długi!";
                            }

                            return true;
                        }
                    }
                ]
            };

            this.addEvents(
                /**
                 * @event tinycreated
                 */
                'tinycreated',

                /**
                 * @event tinychange
                 */
                'tinychange',

                /**
                 * @event tinykeydown
                 */
                'tinykeydown'
            );

            Ext.apply(this, Ext.apply(config, this.initialConfig));
            this.callParent();

            this.on('afterrender', function() {
                var tiny = this.getComponent('tinymce');
                tiny.on('editorcreated', function() {
                    Ext.defer(function() {
                        _this.initEditor()
                        _this.fireEvent('tinycreated', _this, tiny);
                    }, 300);
                });
            });

            // set initial value after tiny is rendered
            if(this.value) {
                this.on('tinycreated', function(panel, field) {
                    field.setValue(_this.value);
                });
            }

            this.on('tinychange', function() {
                _this.countChars();
            })
        },

        /**
         * Initializes editor
         *
         * @private
         */
        initEditor: function()
        {
            var editor = this.items.get(0).ed;
            var _this = this;

            editor.onKeyDown.add(function(ed, e) {
                _this.countChars();
                _this.fireEvent('tinykeydown', _this, ed);
            });

            this.editor = editor;
        },

        /**
         * Config
         *
         * @return {Object}
         */
        getTinyConfig: function()
        {
            var set;
            if (this.tinyConfigSets[this.tinyConfigSet])
            {
                set = this.tinyConfigSets[this.tinyConfigSet];
            }
            else
            {
                set = this.tinyConfigSets['basic'];
            }

            Ext.apply(set, this.tinyBaseConfig);

            return set;
        },

        /**
         * Get dynamic height of tiny
         */
        computeHeight: function()
        {
            return this.height - 40;
        },

        /**
         * Return embeded editor
         *
         * @return TinyMCE.Editor
         */
        getEditor: function()
        {
            return this.editor;
        },

        /**
         * Element of main id container
         *
         * @return {String}
         */
        getElId: function()
        {
            return this.elId;
        },

        /**
         * HTML content
         *
         * @return {String}
         */
        getTinyContent: function()
        {
            return this.getEditor().getContent();
        },

        /**
         * Plaint text content
         *
         * @return {String}
         */
        getTinyPlainContent: function()
        {
            var content = this.getTinyContent();

            return content.replace(/(<([^>]+)>)/ig, "").replace(/&[a-z0-9]+;/ig, " ");
        },

        /**
         * Counts chars
         *
         * @private
         */
        countChars: function()
        {
            this.numChars = this.getTinyPlainContent().length;
        },

        /**
         * Number of chars
         *
         * @return {Number}
         */
        getNumChars: function()
        {
            return this.numChars;
        }
    });

})();<commit_msg>configurable field align<commit_after>/**
 * TinyMCE Form
 *
 * @class HatimeriaAdmin.core.form.TinyMceForm
 * @extends Ext.form.Panel
 */
(function() {

    Ext.define('HatimeriaAdmin.core.form.TinyMceForm', {
        extend: 'Hatimeria.core.form.BaseForm',
        alias: 'widget.hatimeria-tinymce',

        /**
         * Config set
         *
         * @cfg {String} tinyConfigSet
         */
        tinyConfigSet: 'extended',

        /**
         * Initial value for field
         *
         * @cfg {String} value
         */
        value: null,

        /**
         * @cfg {String} fieldLabel Treść
         */
        fieldLabel: 'Treść',

        /**
         * @cfg {String} fieldName
         */
        fieldName: 'description',

        /**
         * @cfg {Number} tinyWidth
         */
        width: 500,

        /**
         * @cfg {Number} tinyHeight
         */
        height: 350,

        /**
         * Margin corrects width of tinyMCE
         *
         * @cfg {Number} marginOffset
         */
        marginOffset: 5,

        /**
         * @cfg {Integer} labelWidth
         */
        labelWidth: 100,

        /**
         * @cfg {Integer} labelWidth
         */
        labelAlign: 'left',

        /**
         * Max text plain length
         * @cfg {Number} maxLength
         */
        maxLength: false,

        /**
         * Allow field blank before submit
         *
         * @cfg {Boolean} allowBlank
         */
        allowBlank: true,

        /**
         * Instance of embeded editor (appears after editor created)
         * @property {TinyMCE.Editor} editor
         * @private
         */
        editor: undefined,

        /**
         * Length of plain content
         * @property {Number} numChars
         * @private
         */
        numChars: 0,

        /**
         * Id of element
         * @property {String}
         * @private
         */
        elId: undefined,

        /**
         * Base config
         *
         * @cfg {Object} tinyBaseConfig
         */
        tinyBaseConfig: {
            theme: 'advanced',
            theme_advanced_toolbar_location: 'top',
            theme_advanced_toolbar_align: 'left',
            theme_advanced_statusbar_location: 'bottom',
            file_browser_callback: 'tinymce_upload',
            theme_advanced_resizing: false,
            convert_urls: false
        },

        /**
         * Defauly config sets
         *
         * @cfg {Object} tinyConfigSets
         */
        tinyConfigSets: {
            extended: {
                plugins: 'pagebreak,style,layer,table,advhr,advimage,advlink,emotions,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras,template',
                theme_advanced_buttons1: 'bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,formatselect,fontsizeselect',
                theme_advanced_buttons2: 'bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor',
                theme_advanced_buttons3: 'tablecontrols,|,hr,sub,sup,|,charmap,media,advhr,|,print,|,ltr,rtl,|',
                theme_advanced_buttons4: 'insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak'
            },
            basic: {
                plugins: 'advimage',
                theme_advanced_buttons1 : "formatselect,fontsizeselect,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,bullist,numlist,undo,redo,link,unlink,image",
                theme_advanced_buttons2 : "",
                theme_advanced_buttons3 : "",
                theme_advanced_buttons4 : ""
            }
        },

        statics: {
            testMe: function() {
                var panel = Ext.create("Hatimeria.core.form.BaseForm", {
                    renderTo: Ext.getBody(),
                    dockedItems: [{
                       dock: 'bottom',
                       xtype: 'toolbar',
                       items: [
                           {
                                text: 'Submit',
                                handler: function() {
                                    console.log(panel.getForm().isValid());
                                }
                           }
                       ]
                    }],
                    items: [
                        Ext.create("HatimeriaAdmin.core.form.TinyMceForm", {
                            maxLength: 2,
                            tinyConfigSet: 'basic',
                            value: 'ala123'
                        }),
                        {
                            xtype: 'numberfield',
                            fieldLabel: 'Numer',
                            name: 'test',
                            allowBlank: false
                        }
                    ],
                    height: 1000,
                    width: 800
                });

                panel.getForm().isValid();
            }
        },

        /**
         * Initialization
         */
        initComponent: function()
        {
            var _this = this;
            this.tinyBaseConfig.height = this.computeHeight();
            var config = {
                border: 0,
                layout: 'auto',
                items: [
                    {
                        itemId: 'tinymce',
                        xtype: 'tinymce',
                        width: ((this.fieldLabel && this.fieldLabel != '') ? (this.width - this.labelWidth - this.marginOffset) : this.width),
                        height: _this.height,
                        fieldLabel: this.fieldLabel,
                        labelWidth: this.labelWidth,
                        labelAlign: this.labelAlign,
                        name: this.fieldName,
                        tinymceSettings: this.getTinyConfig(),
                        allowBlank: this.allowBlank,
                        listeners: {
                            change: function() {
                                _this.fireEvent('tinychange', _this, _this.getEditor())
                            }
                        },
                        validator: function() {
                            if (_this.maxLength && _this.getNumChars() > _this.maxLength)
                            {
                                return "Opis jest zbyt długi!";
                            }

                            return true;
                        }
                    }
                ]
            };

            this.addEvents(
                /**
                 * @event tinycreated
                 */
                'tinycreated',

                /**
                 * @event tinychange
                 */
                'tinychange',

                /**
                 * @event tinykeydown
                 */
                'tinykeydown'
            );

            Ext.apply(this, Ext.apply(config, this.initialConfig));
            this.callParent();

            this.on('afterrender', function() {
                var tiny = this.getComponent('tinymce');
                tiny.on('editorcreated', function() {
                    Ext.defer(function() {
                        _this.initEditor()
                        _this.fireEvent('tinycreated', _this, tiny);
                    }, 300);
                });
            });

            // set initial value after tiny is rendered
            if(this.value) {
                this.on('tinycreated', function(panel, field) {
                    field.setValue(_this.value);
                });
            }

            this.on('tinychange', function() {
                _this.countChars();
            })
        },

        /**
         * Initializes editor
         *
         * @private
         */
        initEditor: function()
        {
            var editor = this.items.get(0).ed;
            var _this = this;

            editor.onKeyDown.add(function(ed, e) {
                _this.countChars();
                _this.fireEvent('tinykeydown', _this, ed);
            });

            this.editor = editor;
        },

        /**
         * Config
         *
         * @return {Object}
         */
        getTinyConfig: function()
        {
            var set;
            if (this.tinyConfigSets[this.tinyConfigSet])
            {
                set = this.tinyConfigSets[this.tinyConfigSet];
            }
            else
            {
                set = this.tinyConfigSets['basic'];
            }

            Ext.apply(set, this.tinyBaseConfig);

            return set;
        },

        /**
         * Get dynamic height of tiny
         */
        computeHeight: function()
        {
            return this.height - 40;
        },

        /**
         * Return embeded editor
         *
         * @return TinyMCE.Editor
         */
        getEditor: function()
        {
            return this.editor;
        },

        /**
         * Element of main id container
         *
         * @return {String}
         */
        getElId: function()
        {
            return this.elId;
        },

        /**
         * HTML content
         *
         * @return {String}
         */
        getTinyContent: function()
        {
            return this.getEditor().getContent();
        },

        /**
         * Plaint text content
         *
         * @return {String}
         */
        getTinyPlainContent: function()
        {
            var content = this.getTinyContent();

            return content.replace(/(<([^>]+)>)/ig, "").replace(/&[a-z0-9]+;/ig, " ");
        },

        /**
         * Counts chars
         *
         * @private
         */
        countChars: function()
        {
            this.numChars = this.getTinyPlainContent().length;
        },

        /**
         * Number of chars
         *
         * @return {Number}
         */
        getNumChars: function()
        {
            return this.numChars;
        }
    });

})();<|endoftext|>


<commit_before>var cadence = require('cadence')
var http = require('http')
var delta = require('delta')
var Conduit = require('conduit/conduit')

module.exports = cadence(function (async, destructible, olio) {
    var logger = require('prolific.logger').createLogger('olio.http')
    var Reactor = require('reactor')
    var Caller = require('conduit/caller')
    var interval = setInterval(function () {}, 1000)
    destructible.destruct.wait(clearInterval.bind(null, interval))
    console.log('will wait')
    olio.sender('olio', cadence(function (async, destructible, inbox, outbox) {
        destructible.monitor('conduit', Conduit, inbox, outbox, null, async())
    }), async())
})
<commit_msg>Tidy `t/client.js`.<commit_after>var cadence = require('cadence')
var Conduit = require('conduit/conduit')

module.exports = cadence(function (async, destructible, olio) {
    olio.sender('olio', cadence(function (async, destructible, inbox, outbox) {
        destructible.monitor('conduit', Conduit, inbox, outbox, null, async())
    }), async())
})
<|endoftext|>


<commit_before>Mocks = {};

HttpClient = {
	setTimeout:function(){},
	setRequestHeader:function(){},
	open: function(){},
	send: function(){}
};

FakeView = {
	render : jasmine.createSpy("render")
};

Mocks.db = { save:jasmine.createSpy(), delete:function(){}, find:jasmine.createSpy()};

Mocks.FakeFile = { write:jasmine.createSpy(), deleteFile:function(){}, exists:jasmine.createSpy()};
FakeGroup = { addTab:function(){}, addEventListener:function(){}, setActiveTab:function(){}, open:function(){} };
FakeiPhone = {AnimationStyle:function(){} };

Titanium = {
	include: function(){},
	Network: {createHTTPClient:function(){ return HttpClient; }},
	Utils: {base64encode:function(){}},
	Filesystem: {applicationDataDirectory: "", getFile: function(){ return Mocks.FakeFile; }},
	UI: {createTabGroup:function(){return FakeGroup; },createWindow:function(){},createTab:function(){},iPhone:FakeiPhone}
};

Ti = {
	API: {info: function() {}},
	Utils: {md5HexDigest:function(){ return "123"; }},
};

function stubDb(response) {
	return jasmine.createSpy().andCallFake(function(obj, callbacks){
		if(typeof callbacks == "function") {
			callbacks(response);
		} else {
			callbacks.error ? callbacks.error(response) : callbacks.success(response);
		};
	});
};
<commit_msg>remove delete keyword from mocks<commit_after>Mocks = {};

HttpClient = {
	setTimeout:function(){},
	setRequestHeader:function(){},
	open: function(){},
	send: function(){}
};

FakeView = {
	render : jasmine.createSpy("render")
};

Mocks.db = { save:jasmine.createSpy(), destroy:function(){}, find:jasmine.createSpy()};

Mocks.FakeFile = { write:jasmine.createSpy(), deleteFile:function(){}, exists:jasmine.createSpy()};
FakeGroup = { addTab:function(){}, addEventListener:function(){}, setActiveTab:function(){}, open:function(){} };
FakeiPhone = {AnimationStyle:function(){} };

Titanium = {
	include: function(){},
	Network: {createHTTPClient:function(){ return HttpClient; }},
	Utils: {base64encode:function(){}},
	Filesystem: {applicationDataDirectory: "", getFile: function(){ return Mocks.FakeFile; }},
	UI: {createTabGroup:function(){return FakeGroup; },createWindow:function(){},createTab:function(){},iPhone:FakeiPhone}
};

Ti = {
	API: {info: function() {}},
	Utils: {md5HexDigest:function(){ return "123"; }},
};

function stubDb(response) {
	return jasmine.createSpy().andCallFake(function(obj, callbacks){
		if(typeof callbacks == "function") {
			callbacks(response);
		} else {
			callbacks.error ? callbacks.error(response) : callbacks.success(response);
		};
	});
};
<|endoftext|>


<commit_before>const yt = require("ytdl-core");
const getInfoAsync = require("util").promisify(yt.getInfo);

const YouTubeRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/\S*(?:(?:\/e(?:mbed)?)?\/|watch\/?\?(?:\S*?&?v=))|youtu\.be\/)([\w-]{11})/;
const DefaultObj = {
  playing: false,
  songs: [],
};

exports.run = async (client, msg, [song]) => {
  const id = YouTubeRegExp.exec(song);
  if (id === null) throw "You must provide a valid YouTube URL.";
  const info = await getInfoAsync(`https://youtu.be/${id[1]}`);

  if (client.queue.has(msg.guild.id) === false) client.queue.set(msg.guild.id, DefaultObj);
  client.queue.get(msg.guild.id).songs.push({ url: song, title: info.title, requester: msg.author.username });

  return msg.send(`🎵 Added **${info.title}** to the queue 🎶`);
};

exports.conf = {
  enabled: true,
  runIn: ["text"],
  aliases: [],
  permLevel: 0,
  botPerms: [],
  requiredFuncs: [],
};

exports.help = {
  name: "add",
  description: "Adds a song the the queue.",
  usage: "<song:str>",
  usageDelim: "",
  extendedHelp: "",
};

exports.init = (client) => {
  client.queue = new client.methods.Collection();
};
<commit_msg>Fix invalid<commit_after>const yt = require("ytdl-core");
const getInfoAsync = require("util").promisify(yt.getInfo);

const YouTubeRegExp = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/\S*(?:(?:\/e(?:mbed)?)?\/|watch\/?\?(?:\S*?&?v=))|youtu\.be\/)([\w-]{11})(?:[^\w-]|$)/;
const DefaultObj = {
  playing: false,
  songs: [],
};

exports.run = async (client, msg, [song]) => {
  const id = YouTubeRegExp.exec(song);
  if (id === null) throw "You must provide a valid YouTube URL.";
  const info = await getInfoAsync(`https://youtu.be/${id[1]}`);

  if (client.queue.has(msg.guild.id) === false) client.queue.set(msg.guild.id, DefaultObj);
  client.queue.get(msg.guild.id).songs.push({ url: song, title: info.title, requester: msg.author.username });

  return msg.send(`🎵 Added **${info.title}** to the queue 🎶`);
};

exports.conf = {
  enabled: true,
  runIn: ["text"],
  aliases: [],
  permLevel: 0,
  botPerms: [],
  requiredFuncs: [],
};

exports.help = {
  name: "add",
  description: "Adds a song the the queue.",
  usage: "<song:str>",
  usageDelim: "",
  extendedHelp: "",
};

exports.init = (client) => {
  client.queue = new client.methods.Collection();
};
<|endoftext|>


<commit_before>// $Id$
// Farbtastic 1.2

jQuery.fn.farbtastic = function (callback) {
  $.farbtastic(this, callback);
  return this;
};

jQuery.farbtastic = function (container, callback) {
  var container = $(container).get(0);
  return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback));
};

jQuery._farbtastic = function (container, callback) {
  // Store farbtastic object
  var fb = this;

  // Insert markup
  $(container).html('

');
  var e = $('.farbtastic', container);
  fb.wheel = $('.wheel', container).get(0);
  // Dimensions
  fb.radius = 84;
  fb.square = 100;
  fb.width = 194;

  // Fix background PNGs in IE6
  if (navigator.appVersion.match(/MSIE [0-6]\./)) {
    $('*', e).each(function () {
      if (this.currentStyle.backgroundImage != 'none') {
        var image = this.currentStyle.backgroundImage;
        image = this.currentStyle.backgroundImage.substring(5, image.length - 2);
        $(this).css({
          'backgroundImage': 'none',
          'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
        });
      }
    });
  }

  /**
   * Link to the given element(s) or callback.
   */
  fb.linkTo = function (callback) {
    // Unbind previous nodes
    if (typeof fb.callback == 'object') {
      $(fb.callback).unbind('keyup', fb.updateValue);
    }

    // Reset color
    fb.color = null;

    // Bind callback or elements
    if (typeof callback == 'function') {
      fb.callback = callback;
    }
    else if (typeof callback == 'object' || typeof callback == 'string') {
      fb.callback = $(callback);
      fb.callback.bind('keyup', fb.updateValue);
      if (fb.callback.get(0).value) {
        fb.setColor(fb.callback.get(0).value);
      }
    }
    return this;
  };
  fb.updateValue = function (event) {
    if (this.value && this.value != fb.color) {
      fb.setColor(this.value);
    }
  };

  /**
   * Change color with HTML syntax #123456
   */
  fb.setColor = function (color) {
    var unpack = fb.unpack(color);
    if (fb.color != color && unpack) {
      fb.color = color;
      fb.rgb = unpack;
      fb.hsl = fb.RGBToHSL(fb.rgb);
      fb.updateDisplay();
    }
    return this;
  };

  /**
   * Change color with HSL triplet [0..1, 0..1, 0..1]
   */
  fb.setHSL = function (hsl) {
    fb.hsl = hsl;
    fb.rgb = fb.HSLToRGB(hsl);
    fb.color = fb.pack(fb.rgb);
    fb.updateDisplay();
    return this;
  };

  /////////////////////////////////////////////////////

  /**
   * Retrieve the coordinates of the given event relative to the center
   * of the widget.
   */
  fb.widgetCoords = function (event) {
    var x, y;
    var el = event.target || event.srcElement;
    var reference = fb.wheel;

    // If the offset from the relative element is undefined calculate it.
    if ( typeof event.offsetX == 'undefined' && typeof event.offsetY == 'undefined' ) {
      var offset = $(event.target).offset(false);
      event.offsetX = event.pageX - offset.left;
      event.offsetY = event.pageY - offset.top;
    }

    // Use offset coordinates and find common offsetParent
    var pos = { x: event.offsetX, y: event.offsetY };

    // Send the coordinates upwards through the offsetParent chain.
    var e = el;
    while (e) {
      e.mouseX = pos.x;
      e.mouseY = pos.y;
      pos.x += e.offsetLeft;
      pos.y += e.offsetTop;
      e = e.offsetParent;
    }

    // Look for the coordinates starting from the wheel widget.
    var e = reference;
    var offset = { x: 0, y: 0 };
    while (e) {
      if (typeof e.mouseX != 'undefined') {
        x = e.mouseX - offset.x;
        y = e.mouseY - offset.y;
        break;
      }
      offset.x += e.offsetLeft;
      offset.y += e.offsetTop;
      e = e.offsetParent;
    }

    // Reset stored coordinates
    e = el;
    while (e) {
      e.mouseX = undefined;
      e.mouseY = undefined;
      e = e.offsetParent;
    }

    // Subtract distance to middle
    return { x: x - fb.width / 2, y: y - fb.width / 2 };
  };

  /**
   * Mousedown handler
   */
  fb.mousedown = function (event) {
    // Capture mouse
    if (!document.dragging) {
      $(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup);
      document.dragging = true;
    }

    // Check which area is being dragged
    var pos = fb.widgetCoords(event);
    fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square;

    // Process
    fb.mousemove(event);
    return false;
  };

  /**
   * Mousemove handler
   */
  fb.mousemove = function (event) {
    // Get coordinates relative to color picker center
    var pos = fb.widgetCoords(event);

    // Set new HSL parameters
    if (fb.circleDrag) {
      var hue = Math.atan2(pos.x, -pos.y) / 6.28;
      if (hue < 0) hue += 1;
      fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]);
    }
    else {
      var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5));
      var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5));
      fb.setHSL([fb.hsl[0], sat, lum]);
    }
    return false;
  };

  /**
   * Mouseup handler
   */
  fb.mouseup = function () {
    // Uncapture mouse
    $(document).unbind('mousemove', fb.mousemove);
    $(document).unbind('mouseup', fb.mouseup);
    document.dragging = false;
  };

  /**
   * Update the markers and styles
   */
  fb.updateDisplay = function () {
    // Markers
    var angle = fb.hsl[0] * 6.28;
    $('.h-marker', e).css({
      left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px',
      top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px'
    });

    $('.sl-marker', e).css({
      left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px',
      top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px'
    });

    // Saturation/Luminance gradient
    $('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5])));

    // Linked elements or callback
    if (typeof fb.callback == 'object') {
      // Set background/foreground color
      $(fb.callback).css({
        backgroundColor: fb.color,
        color: fb.hsl[2] > 0.5 ? '#000' : '#fff'
      });

      // Change linked value
      $(fb.callback).each(function() {
        if (this.value && this.value != fb.color) {
          this.value = fb.color;
        }
      });
    }
    else if (typeof fb.callback == 'function') {
      fb.callback.call(fb, fb.color);
    }
  };

  /* Various color utility functions */
  fb.pack = function (rgb) {
    var r = Math.round(rgb[0] * 255);
    var g = Math.round(rgb[1] * 255);
    var b = Math.round(rgb[2] * 255);
    return '#' + (r < 16 ? '0' : '') + r.toString(16) +
           (g < 16 ? '0' : '') + g.toString(16) +
           (b < 16 ? '0' : '') + b.toString(16);
  };

  fb.unpack = function (color) {
    if (color.length == 7) {
      return [parseInt('0x' + color.substring(1, 3)) / 255,
        parseInt('0x' + color.substring(3, 5)) / 255,
        parseInt('0x' + color.substring(5, 7)) / 255];
    }
    else if (color.length == 4) {
      return [parseInt('0x' + color.substring(1, 2)) / 15,
        parseInt('0x' + color.substring(2, 3)) / 15,
        parseInt('0x' + color.substring(3, 4)) / 15];
    }
  };

  fb.HSLToRGB = function (hsl) {
    var m1, m2, r, g, b;
    var h = hsl[0], s = hsl[1], l = hsl[2];
    m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s;
    m1 = l * 2 - m2;
    return [this.hueToRGB(m1, m2, h+0.33333),
        this.hueToRGB(m1, m2, h),
        this.hueToRGB(m1, m2, h-0.33333)];
  };

  fb.hueToRGB = function (m1, m2, h) {
    h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
    if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
    if (h * 2 < 1) return m2;
    if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
    return m1;
  };

  fb.RGBToHSL = function (rgb) {
    var min, max, delta, h, s, l;
    var r = rgb[0], g = rgb[1], b = rgb[2];
    min = Math.min(r, Math.min(g, b));
    max = Math.max(r, Math.max(g, b));
    delta = max - min;
    l = (min + max) / 2;
    s = 0;
    if (l > 0 && l < 1) {
      s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
    }
    h = 0;
    if (delta > 0) {
      if (max == r && max != g) h += (g - b) / delta;
      if (max == g && max != b) h += (2 + (b - r) / delta);
      if (max == b && max != r) h += (4 + (r - g) / delta);
      h /= 6;
    }
    return [h, s, l];
  };

  // Install mousedown handler (the others are set on the document on-demand)
  $('*', e).mousedown(fb.mousedown);

    // Init color
  fb.setColor('#000000');

  // Set linked elements/callback
  if (callback) {
    fb.linkTo(callback);
  }
};<commit_msg>- Patch #323965 by mfer: update farbtastic widgetCoords to use jQuery.<commit_after>// $Id$
// Farbtastic 1.2

jQuery.fn.farbtastic = function (callback) {
  $.farbtastic(this, callback);
  return this;
};

jQuery.farbtastic = function (container, callback) {
  var container = $(container).get(0);
  return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback));
};

jQuery._farbtastic = function (container, callback) {
  // Store farbtastic object
  var fb = this;

  // Insert markup
  $(container).html('

');
  var e = $('.farbtastic', container);
  fb.wheel = $('.wheel', container).get(0);
  // Dimensions
  fb.radius = 84;
  fb.square = 100;
  fb.width = 194;

  // Fix background PNGs in IE6
  if (navigator.appVersion.match(/MSIE [0-6]\./)) {
    $('*', e).each(function () {
      if (this.currentStyle.backgroundImage != 'none') {
        var image = this.currentStyle.backgroundImage;
        image = this.currentStyle.backgroundImage.substring(5, image.length - 2);
        $(this).css({
          'backgroundImage': 'none',
          'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')"
        });
      }
    });
  }

  /**
   * Link to the given element(s) or callback.
   */
  fb.linkTo = function (callback) {
    // Unbind previous nodes
    if (typeof fb.callback == 'object') {
      $(fb.callback).unbind('keyup', fb.updateValue);
    }

    // Reset color
    fb.color = null;

    // Bind callback or elements
    if (typeof callback == 'function') {
      fb.callback = callback;
    }
    else if (typeof callback == 'object' || typeof callback == 'string') {
      fb.callback = $(callback);
      fb.callback.bind('keyup', fb.updateValue);
      if (fb.callback.get(0).value) {
        fb.setColor(fb.callback.get(0).value);
      }
    }
    return this;
  };
  fb.updateValue = function (event) {
    if (this.value && this.value != fb.color) {
      fb.setColor(this.value);
    }
  };

  /**
   * Change color with HTML syntax #123456
   */
  fb.setColor = function (color) {
    var unpack = fb.unpack(color);
    if (fb.color != color && unpack) {
      fb.color = color;
      fb.rgb = unpack;
      fb.hsl = fb.RGBToHSL(fb.rgb);
      fb.updateDisplay();
    }
    return this;
  };

  /**
   * Change color with HSL triplet [0..1, 0..1, 0..1]
   */
  fb.setHSL = function (hsl) {
    fb.hsl = hsl;
    fb.rgb = fb.HSLToRGB(hsl);
    fb.color = fb.pack(fb.rgb);
    fb.updateDisplay();
    return this;
  };

  /////////////////////////////////////////////////////

  /**
   * Retrieve the coordinates of the given event relative to the center
   * of the widget.
   */
  fb.widgetCoords = function (event) {
    var offset = $(fb.wheel).offset();
    return { x: (event.pageX - offset.left) - fb.width / 2, y: (event.pageY - offset.top) - fb.width / 2 };
  };

  /**
   * Mousedown handler
   */
  fb.mousedown = function (event) {
    // Capture mouse
    if (!document.dragging) {
      $(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup);
      document.dragging = true;
    }

    // Check which area is being dragged
    var pos = fb.widgetCoords(event);
    fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square;

    // Process
    fb.mousemove(event);
    return false;
  };

  /**
   * Mousemove handler
   */
  fb.mousemove = function (event) {
    // Get coordinates relative to color picker center
    var pos = fb.widgetCoords(event);

    // Set new HSL parameters
    if (fb.circleDrag) {
      var hue = Math.atan2(pos.x, -pos.y) / 6.28;
      if (hue < 0) hue += 1;
      fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]);
    }
    else {
      var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5));
      var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5));
      fb.setHSL([fb.hsl[0], sat, lum]);
    }
    return false;
  };

  /**
   * Mouseup handler
   */
  fb.mouseup = function () {
    // Uncapture mouse
    $(document).unbind('mousemove', fb.mousemove);
    $(document).unbind('mouseup', fb.mouseup);
    document.dragging = false;
  };

  /**
   * Update the markers and styles
   */
  fb.updateDisplay = function () {
    // Markers
    var angle = fb.hsl[0] * 6.28;
    $('.h-marker', e).css({
      left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px',
      top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px'
    });

    $('.sl-marker', e).css({
      left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px',
      top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px'
    });

    // Saturation/Luminance gradient
    $('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5])));

    // Linked elements or callback
    if (typeof fb.callback == 'object') {
      // Set background/foreground color
      $(fb.callback).css({
        backgroundColor: fb.color,
        color: fb.hsl[2] > 0.5 ? '#000' : '#fff'
      });

      // Change linked value
      $(fb.callback).each(function() {
        if (this.value && this.value != fb.color) {
          this.value = fb.color;
        }
      });
    }
    else if (typeof fb.callback == 'function') {
      fb.callback.call(fb, fb.color);
    }
  };

  /* Various color utility functions */
  fb.pack = function (rgb) {
    var r = Math.round(rgb[0] * 255);
    var g = Math.round(rgb[1] * 255);
    var b = Math.round(rgb[2] * 255);
    return '#' + (r < 16 ? '0' : '') + r.toString(16) +
           (g < 16 ? '0' : '') + g.toString(16) +
           (b < 16 ? '0' : '') + b.toString(16);
  };

  fb.unpack = function (color) {
    if (color.length == 7) {
      return [parseInt('0x' + color.substring(1, 3)) / 255,
        parseInt('0x' + color.substring(3, 5)) / 255,
        parseInt('0x' + color.substring(5, 7)) / 255];
    }
    else if (color.length == 4) {
      return [parseInt('0x' + color.substring(1, 2)) / 15,
        parseInt('0x' + color.substring(2, 3)) / 15,
        parseInt('0x' + color.substring(3, 4)) / 15];
    }
  };

  fb.HSLToRGB = function (hsl) {
    var m1, m2, r, g, b;
    var h = hsl[0], s = hsl[1], l = hsl[2];
    m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s;
    m1 = l * 2 - m2;
    return [this.hueToRGB(m1, m2, h+0.33333),
        this.hueToRGB(m1, m2, h),
        this.hueToRGB(m1, m2, h-0.33333)];
  };

  fb.hueToRGB = function (m1, m2, h) {
    h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h);
    if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
    if (h * 2 < 1) return m2;
    if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6;
    return m1;
  };

  fb.RGBToHSL = function (rgb) {
    var min, max, delta, h, s, l;
    var r = rgb[0], g = rgb[1], b = rgb[2];
    min = Math.min(r, Math.min(g, b));
    max = Math.max(r, Math.max(g, b));
    delta = max - min;
    l = (min + max) / 2;
    s = 0;
    if (l > 0 && l < 1) {
      s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l));
    }
    h = 0;
    if (delta > 0) {
      if (max == r && max != g) h += (g - b) / delta;
      if (max == g && max != b) h += (2 + (b - r) / delta);
      if (max == b && max != r) h += (4 + (r - g) / delta);
      h /= 6;
    }
    return [h, s, l];
  };

  // Install mousedown handler (the others are set on the document on-demand)
  $('*', e).mousedown(fb.mousedown);

    // Init color
  fb.setColor('#000000');

  // Set linked elements/callback
  if (callback) {
    fb.linkTo(callback);
  }
};<|endoftext|>