<commit_before>"use strict";

define(["jquery", "underscore", "oauth", "util"], function($, _, _oauth, Util) {
    /**
     * Client side library for the KA API
     */
    var APIClient = {
        oauth: {
            consumerKey: "",
            consumerSecret: "",
            token: Util.getParameterByName("oauth_token"),
            tokenSecret: Util.getParameterByName("oauth_token_secret"),
            oauthVerifier: Util.getParameterByName("oauth_verifier")
        },
        _localStorageAuthName: "oauth",
        /**
         * Load oauth info from local storage.
         */
        _loadAuth: function() {
            var oauth = localStorage.getItem(this._localStorageAuthName);
            if (oauth) {
                this.oauth = JSON.parse(oauth);
            }
        },
        /**
         * Save oauth info to local storage
         */
        _saveAuth: function() {
            localStorage.setItem(this._localStorageAuthName, JSON.stringify(this.oauth));
        },
        /**
         * Obtains the locally stored secrets file.
         * TODO: We could just make the secrets file javascript and include the script!
         */
        _getSecrets: function() {
            // First check if we have the info from the local storage values
            // If so just resolve from that.
            if (this.oauth.consumerKey && this.oauth.consumerSecret) {
                Util.log("resolving with local secrets info");
                return $.Deferred().resolve(this.oauth).promise();
            }

            // Otherwise request the info from the secrets.json file
            return $.ajax({
                url: "/secrets.json",
                timeout: 15000,
                dataType: "json",
            });
        },
        /**
         * Obtains the access token using the request token and oauth verifier.
         */
        _getAccessToken: function() {
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: "GET",
                url: this.API_BASE + "/auth/access_token",
                oauthCallback: this._oauthCallback,
                timeout: 15000,
                success: (data) => {
                    this.oauth.token = Util.getParameterByName("oauth_token", data);
                    this.oauth.tokenSecret = Util.getParameterByName("oauth_token_secret", data);
                    delete this.oauth.oauthVerifier;
                    d.resolve();
                },
                error: (xhr, status) => {
                    Util.error(`error: ${status}: %o`, xhr);
                    d.reject();
                }
            }));
            return d.promise();
        },
        /**
         * Determines based on the known oauth info, if the user is signed in.
         */
        isSignedIn: function() {
            return !!(this.oauth.consumerKey &&
                this.oauth.consumerSecret &&
                this.oauth.token &&
                this.oauth.tokenSecret);
        },
        /**
         * Initializes APIClient by loading in the secrets file and any saved
         * auth information from local storage.
         *
         * @return a promise which resolves when the object is initialized.
         */
        init: function() {
            // If a login is not in progress, then load the auth info
            var oauthVerifier = Util.getParameterByName("oauth_token");
            if (!oauthVerifier) {
                this._loadAuth();
            }
            var d = $.Deferred();
            this._oauthCallback = window.location.href.split("#")[0].split('?')[0];
            if (Util.isFirefoxOS()) {
                this._oauthCallback = "http://firefoxos.non-existent-domain-asdfg.com/authenticated.html";
            }

            this._getSecrets().done((keyData) => {
                this.oauth.consumerKey = keyData.consumerKey;
                this.oauth.consumerSecret = keyData.consumerSecret;

                // TODO: Only do access token stuff if we don't have local storage values
                if (this.oauth.oauthVerifier) {
                    this._getAccessToken().done(() => {
                        this._saveAuth();
                        d.resolve();
                    }).fail(() => {
                        // Even if we failed, we should resolve because this
                        // indicates we are initialized successfully, justnot
                        // signed in.
                        d.resolve();
                    });
                } else {
                    d.resolve();
                }
            }).fail(() => {
                // We should always be able to obtain secrets.json info!
                Util.warn("Could not obtain secrets");
                d.reject();
            });
            return d.promise();
        },
        /**
         * Signs the user in by redirecting them.
         * Currently this function does NOT return, it redirects the page
         *
         * TODO: I think we can do this by appending an iframe instead of setting
         * the location directly.
         */
        signIn: function() {
            // Start the oauth process by redirecting them to the request_token url
            var url = $.getURL($.extend( {}, this.oauth, {
                url: this.API_BASE + "/auth/request_token",
                oauthCallback: this._oauthCallback
            }));
            window.location = url;
        },
        /**
         * Resets the oauth info and clears out oauth local storage.
         */
        signOut: function() {
            this.oauth.token = "";
            this.oauth.tokenSecret = "";
            this._saveAuth();
        },
        /**
         * Performs an oauth basic API call using the logged in oauth info
         *
         * @return a promise with the results of the API call
         */
        _basicAPICall: function(url, extraParams, method, dataType) {
            extraParams = extraParams || {};
            dataType = dataType || "json";
            if (_.isUndefined(method)) {
                method = "GET";
            }

            // Add a lang parameter to tell the KA API which langauge we want
            var lang = Util.getLang();
            if (lang) {
                extraParams["lang"] = lang;
            }

            for (var p in extraParams) {
                if (extraParams.hasOwnProperty(p)) {
                    url = Util.appendQueryParam(url, p, extraParams[p]);
                }
            }
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: method,
                url: url,
                timeout: 45000,
                dataType,
                success: (data) => {
                    d.resolve(data);
                },
                error: function(xhr, status) {
                    Util.error(`error: ${status}: %o`, xhr);
                    d.reject();
                }
            }));
            return d.promise();
        },
        /**
         * Obtains the user progress summary. That is to say started and
         * completed information.
         *
         * @return a promise with the results
         */
        getUserProgress: function() {
            var extraParams = {
                kind: "Video,Article"
            };
            return this._basicAPICall(this.API_V1_BASE + "/user/progress_summary", extraParams);
        },
        /**
         * Obtains basic user profile information.
         */
        getUserInfo: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user");
        },
        /**
         * Obtains the installed local topic tree.
         *
         * @param jsOnly true if the installed file to load is pure JavaScript.
         * @return a promise with the topic tree
         */
        getInstalledTopicTree: function(jsOnly) {
            var filename = `/data/topic-tree`;
            var lang = Util.getLang();
            if (lang) {
                filename += "-" + lang;
            }
            filename += jsOnly ? ".min.js" : ".min.json";
            Util.log("Getting installed topic tree from: " + filename);
            return this._basicAPICall(filename, undefined, undefined, jsOnly ? "text" : "json");
        },
        /**
         * Obtains the topic tree from the server.
         *
         * @return a promise with the topic tree
         */
        getTopicTree: function() {
            return this._basicAPICall(this.API_V1_BASE + "/fxos/topictree");
        },
        /**
         * Obtains a transcript for a video.
         *
         * @param youTubeId The id of the youtube video to obtain the transcript for.
         * @return a promise with the transcript
         */
        getVideoTranscript: function(youTubeId) {
            return this._basicAPICall(this.API_V1_BASE + `/videos/${youTubeId}/transcript`);
        },
        /**
         * Obtains an article
         *
         * @param articleId The id of the article to obtain information on.
         * @return a promise with the status and other information
         */
        getArticle: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + "/articles/" + articleId);
        },
        /**
         * Obtains an exercise
         *
         * @param exerciseId The id of the exercise to obtain information on.
         * @return a promise with the status and other information
         */
        getExerciseByName: function(exerciseName) {
            return this._basicAPICall(this.API_V1_BASE + "/exercises/" + exerciseName);
        },
        /**
         * Obtains an exercise
         *
         * @param exerciseId The id of the exercise to obtain information on.
         * @return a promise with the status and other information
         */
        getAssessmentItem: function(assessmentId) {
            return this._basicAPICall(this.API_V1_BASE + "/assessment_items/" + assessmentId);
        },
        /**
         * Marks an article as read.
         *
         * @param articleId The id of the article to mark as completed.
         * @return a promise with the status and other information
         */
        reportArticleRead: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + `/user/article/${articleId}/log`, undefined, "POST");
        },
        /**
         * Obtains the last second watched for each video the user has watched
         * at least a part of.
         *
         * @return a promise with the results
         */
        getUserVideos: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user/videos");
        },
        /**
         * Reports progress and completion on a video.
         *
         * @param videoId The ID of the video to report progress on
         * @param youTubeId The youtube ID of the video to report progress on
         * @param duration The duration of the video (TODO: unused)
         * @param secondswatched The number of seconds watched
         * @param lastSecondWatched The last second watched of the video (for resume support)
         * @return a promise with some extra information like the points earned.
         */
        reportVideoProgress: function(videoId, youTubeId, duration, secondsWatched, lastSecondWatched) {
            var extraParams = {
                seconds_watched: secondsWatched.toString(),
                last_second_watched: lastSecondWatched.toString()
            };
            return this._basicAPICall(this.API_V1_BASE + `/user/videos/${youTubeId}/log`, extraParams, "POST");
        },
        API_BASE: "https://www.khanacademy.org/api",
        API_V1_BASE: "https://www.khanacademy.org/api/v1"
        //API_BASE: "http://192.168.1.131:8080/api",
        //API_V1_BASE: "http://192.168.1.131:8080/api/v1",
        //API_BASE: "http://stable.ka.local:8080/api",
        //API_V1_BASE: "http://stable.ka.local:8080/api/v1"
    };

    return APIClient;
});
<commit_msg>Start to implement API for exercise progress reporting<commit_after>"use strict";

define(["jquery", "underscore", "oauth", "util"], function($, _, _oauth, Util) {
    /**
     * Client side library for the KA API
     */
    var APIClient = {
        oauth: {
            consumerKey: "",
            consumerSecret: "",
            token: Util.getParameterByName("oauth_token"),
            tokenSecret: Util.getParameterByName("oauth_token_secret"),
            oauthVerifier: Util.getParameterByName("oauth_verifier")
        },
        _localStorageAuthName: "oauth",
        /**
         * Load oauth info from local storage.
         */
        _loadAuth: function() {
            var oauth = localStorage.getItem(this._localStorageAuthName);
            if (oauth) {
                this.oauth = JSON.parse(oauth);
            }
        },
        /**
         * Save oauth info to local storage
         */
        _saveAuth: function() {
            localStorage.setItem(this._localStorageAuthName, JSON.stringify(this.oauth));
        },
        /**
         * Obtains the locally stored secrets file.
         * TODO: We could just make the secrets file javascript and include the script!
         */
        _getSecrets: function() {
            // First check if we have the info from the local storage values
            // If so just resolve from that.
            if (this.oauth.consumerKey && this.oauth.consumerSecret) {
                Util.log("resolving with local secrets info");
                return $.Deferred().resolve(this.oauth).promise();
            }

            // Otherwise request the info from the secrets.json file
            return $.ajax({
                url: "/secrets.json",
                timeout: 15000,
                dataType: "json",
            });
        },
        /**
         * Obtains the access token using the request token and oauth verifier.
         */
        _getAccessToken: function() {
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: "GET",
                url: this.API_BASE + "/auth/access_token",
                oauthCallback: this._oauthCallback,
                timeout: 15000,
                success: (data) => {
                    this.oauth.token = Util.getParameterByName("oauth_token", data);
                    this.oauth.tokenSecret = Util.getParameterByName("oauth_token_secret", data);
                    delete this.oauth.oauthVerifier;
                    d.resolve();
                },
                error: (xhr, status) => {
                    Util.error(`error: ${status}: %o`, xhr);
                    d.reject();
                }
            }));
            return d.promise();
        },
        /**
         * Determines based on the known oauth info, if the user is signed in.
         */
        isSignedIn: function() {
            return !!(this.oauth.consumerKey &&
                this.oauth.consumerSecret &&
                this.oauth.token &&
                this.oauth.tokenSecret);
        },
        /**
         * Initializes APIClient by loading in the secrets file and any saved
         * auth information from local storage.
         *
         * @return a promise which resolves when the object is initialized.
         */
        init: function() {
            // If a login is not in progress, then load the auth info
            var oauthVerifier = Util.getParameterByName("oauth_token");
            if (!oauthVerifier) {
                this._loadAuth();
            }
            var d = $.Deferred();
            this._oauthCallback = window.location.href.split("#")[0].split('?')[0];
            if (Util.isFirefoxOS()) {
                this._oauthCallback = "http://firefoxos.non-existent-domain-asdfg.com/authenticated.html";
            }

            this._getSecrets().done((keyData) => {
                this.oauth.consumerKey = keyData.consumerKey;
                this.oauth.consumerSecret = keyData.consumerSecret;

                // TODO: Only do access token stuff if we don't have local storage values
                if (this.oauth.oauthVerifier) {
                    this._getAccessToken().done(() => {
                        this._saveAuth();
                        d.resolve();
                    }).fail(() => {
                        // Even if we failed, we should resolve because this
                        // indicates we are initialized successfully, justnot
                        // signed in.
                        d.resolve();
                    });
                } else {
                    d.resolve();
                }
            }).fail(() => {
                // We should always be able to obtain secrets.json info!
                Util.warn("Could not obtain secrets");
                d.reject();
            });
            return d.promise();
        },
        /**
         * Signs the user in by redirecting them.
         * Currently this function does NOT return, it redirects the page
         *
         * TODO: I think we can do this by appending an iframe instead of setting
         * the location directly.
         */
        signIn: function() {
            // Start the oauth process by redirecting them to the request_token url
            var url = $.getURL($.extend( {}, this.oauth, {
                url: this.API_BASE + "/auth/request_token",
                oauthCallback: this._oauthCallback
            }));
            window.location = url;
        },
        /**
         * Resets the oauth info and clears out oauth local storage.
         */
        signOut: function() {
            this.oauth.token = "";
            this.oauth.tokenSecret = "";
            this._saveAuth();
        },
        /**
         * Performs an oauth basic API call using the logged in oauth info
         *
         * @return a promise with the results of the API call
         */
        _basicAPICall: function(url, extraParams, method, dataType) {
            extraParams = extraParams || {};
            dataType = dataType || "json";
            if (_.isUndefined(method)) {
                method = "GET";
            }

            // Add a lang parameter to tell the KA API which langauge we want
            var lang = Util.getLang();
            if (lang) {
                extraParams["lang"] = lang;
            }

            for (var p in extraParams) {
                if (extraParams.hasOwnProperty(p)) {
                    url = Util.appendQueryParam(url, p, extraParams[p]);
                }
            }
            var d = $.Deferred();
            $.oauth($.extend( {}, this.oauth, {
                type: method,
                url: url,
                timeout: 45000,
                dataType,
                success: (data) => {
                    d.resolve(data);
                },
                error: function(xhr, status) {
                    Util.error(`error: ${status}: %o`, xhr);
                    d.reject();
                }
            }));
            return d.promise();
        },
        /**
         * Obtains the user progress summary. That is to say started and
         * completed information.
         *
         * @return a promise with the results
         */
        getUserProgress: function() {
            var extraParams = {
                kind: "Video,Article"
            };
            return this._basicAPICall(this.API_V1_BASE + "/user/progress_summary", extraParams);
        },
        /**
         * Obtains basic user profile information.
         */
        getUserInfo: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user");
        },
        /**
         * Obtains the installed local topic tree.
         *
         * @param jsOnly true if the installed file to load is pure JavaScript.
         * @return a promise with the topic tree
         */
        getInstalledTopicTree: function(jsOnly) {
            var filename = `/data/topic-tree`;
            var lang = Util.getLang();
            if (lang) {
                filename += "-" + lang;
            }
            filename += jsOnly ? ".min.js" : ".min.json";
            Util.log("Getting installed topic tree from: " + filename);
            return this._basicAPICall(filename, undefined, undefined, jsOnly ? "text" : "json");
        },
        /**
         * Obtains the topic tree from the server.
         *
         * @return a promise with the topic tree
         */
        getTopicTree: function() {
            return this._basicAPICall(this.API_V1_BASE + "/fxos/topictree");
        },
        /**
         * Obtains a transcript for a video.
         *
         * @param youTubeId The id of the youtube video to obtain the transcript for.
         * @return a promise with the transcript
         */
        getVideoTranscript: function(youTubeId) {
            return this._basicAPICall(this.API_V1_BASE + `/videos/${youTubeId}/transcript`);
        },
        /**
         * Obtains an article
         *
         * @param articleId The id of the article to obtain information on.
         * @return a promise with the status and other information
         */
        getArticle: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + "/articles/" + articleId);
        },
        /**
         * Obtains an exercise
         *
         * @param exerciseId The id of the exercise to obtain information on.
         * @return a promise with the status and other information
         */
        getExerciseByName: function(exerciseName) {
            return this._basicAPICall(this.API_V1_BASE + "/exercises/" + exerciseName);
        },
        /**
         * Obtains an exercise
         *
         * @param exerciseId The id of the exercise to obtain information on.
         * @return a promise with the status and other information
         */
        getAssessmentItem: function(assessmentId) {
            return this._basicAPICall(this.API_V1_BASE + "/assessment_items/" + assessmentId);
        },
        /**
         * Marks an article as read.
         *
         * @param articleId The id of the article to mark as completed.
         * @return a promise with the status and other information
         */
        reportArticleRead: function(articleId) {
            return this._basicAPICall(this.API_V1_BASE + `/user/article/${articleId}/log`, undefined, "POST");
        },
        /**
         * Obtains the last second watched for each video the user has watched
         * at least a part of.
         *
         * @return a promise with the results
         */
        getUserVideos: function() {
            return this._basicAPICall(this.API_V1_BASE + "/user/videos");
        },
        /**
         * Reports progress and completion on a video.
         *
         * @param videoId The ID of the video to report progress on
         * @param youTubeId The youtube ID of the video to report progress on
         * @param duration The duration of the video (TODO: unused)
         * @param secondswatched The number of seconds watched
         * @param lastSecondWatched The last second watched of the video (for resume support)
         * @return a promise with some extra information like the points earned.
         */
        reportVideoProgress: function(videoId, youTubeId, duration, secondsWatched, lastSecondWatched) {
            var extraParams = {
                seconds_watched: secondsWatched.toString(),
                last_second_watched: lastSecondWatched.toString()
            };
            return this._basicAPICall(this.API_V1_BASE + `/user/videos/${youTubeId}/log`, extraParams, "POST");
        },

        reportExerciseProgress: function(exerciseName, problemNumber, assessmentSHA, assessmentId, secondsTaken, hintsUsedCount, isCorrect, attemptNumber) {
            var extraParams = {
                casing: "camel",
                sha1: assessmentSHA,
                seed: assessmentId,
                seconds_taken: secondsTaken,
                count_hints: hintsUsedCount,
                complete: isCorrect ? 1 : 0,
                attemptNumber: attemptNumber,
                card: "{}",
                attempt_content: [],
                problem_type: "Type 2",//???
                task_id: 5436994858516480,//???
                user_assessment_key: "",
                skipped: 0,
                opt_out: 0
            };
            return this._basicAPICall(this.API_INTERNAL_BASE + `/user/exercises/${exerciseName}/problems/${problemNumber}/attempt`, extraParams, "POST");
            //https://www.khanacademy.org/api/internal/user/exercises/count-to-100/problems/14/attempt
            //https://www.khanacademy.org/api/internal/user/exercises/count-to-100/problems/14/attempt?lang=en&_=1420051071066
            /*
            "sha1: " + assessmentSHA (assessmentItem.sha)
            "seed: " + assessmentId (assessmentItem.id)
            problem_type:Type 2???
            casing: camel
            complete: isCorrect ? '1' : '0'
            count_hints: hintsUsedCount
            time_taken: secondsTaken
            attempt_number: attemptNumber (1)
            attempt_content:[[{"choices":[{"content":"$60$","originalIndex":0,"isNoneOfTheAbove":false},{"content":"$90$","originalIndex":2,"isNoneOfTheAbove":false},{"content":"$40$","originalIndex":1,"isNoneOfTheAbove":false}],"noneOfTheAbove":false,"onePerLine":true,"multipleSelect":false,"values":[false,true,false],"widgetId":"radio 1","problemNum":14,"enabledFeatures":{"toolTipFormats":true,"useMathQuill":true},"apiOptions":{"fancyDropdowns":false,"staticRender":false,"enableOldAnswerTypes":false,"readOnly":false},"questionCompleted":false,"reviewModeRubric":null,"displayCount":null,"deselectEnabled":false,"noneOfTheAboveIndex":null,"noneOfTheAboveSelected":false}],[]]
            task_id:5436994858516480???
            user_mission_id:m:Brian R. Bondy:123abc123abc123abc???
            card:???{"cardType":"problem","exerciseName":"count-to-100","leavesEarned":-1,"done":true,"growthHeader":"","assessmentMode":false,"includeSkipButton":false,"includeOptOutButton":false,"includeWorkedExampleButton":false,"showGrowthHeader":false,"showProblemsRemaining":false}
            user_assessment_key:
            skipped:0
            opt_out:0
            */

            /* Response:
            {
                "streak": 10,
                "snoozeTime": null,
                "masteryPoints": 3,
                "maximumExerciseProgressDt": "2014-03-26T16:16:17Z",
                "totalDone": 14,
                "lastCountHints": 0,
                "exerciseModel": {
                    "translatedShortDisplayName": "Count to 10",
                    "usesStreakSelection": false,
                    "vPosition": -2,
                    "relativeUrl": "/exercise/count-to-100",
                    "fileName": null,
                    "authorName": "Gail Hargrave",
                    "creationDate": "2014-11-10T22:20:36Z",
                    "usesAssessmentItems": true,
                    "kaUrl": "https://www.khanacademy.org/exercise/count-to-100",
                    "shortDisplayName": "Count to 10",
                    "translatedTitle": "Count to 100",
                    "authorKey": "123abc",
                    "translatedDescriptionHtml": "Can you count to 100?",
                    "id": "xa4413411",
                    "isQuiz": false,
                    "displayName": "Count to 100",
                    "trackingDocumentUrl": "",
                    "descriptionHtml": "Can you count to 100?",
                    "doNotPublish": false,
                    "tags": [],
                    "progressKey": "exa4413411",
                    "suggestedCompletionCriteria": "num_correct_in_a_row_5",
                    "editSlug": "edit/e/xa4413411",
                    "summative": false,
                    "live": true,
                    "translatedDescription":
                    "Can you count to 100?",
                    "prettyDisplayName": "Count to 100",
                    "deletedModTime": null,
                    "allAssessmentItems": [
                    ...
            */
        },
        API_BASE: "https://www.khanacademy.org/api",
        API_V1_BASE: "https://www.khanacademy.org/api/v1",
        API_INTERNAL_BASE: "https://www.khanacademy.org/api/internal",
        //API_BASE: "http://192.168.1.131:8080/api",
        //API_V1_BASE: "http://192.168.1.131:8080/api/v1",
        //API_BASE: "http://stable.ka.local:8080/api",
        //API_V1_BASE: "http://stable.ka.local:8080/api/v1"
    };

    return APIClient;
});
<|endoftext|>


<commit_before>function initOnClick() {
    $('.PromotionBoxGamified').click(function () {
        document.location = $(this).find('.button').attr('href');
        return false;
    });
}

$(document).ready(function () {
    initOnClick();

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () { initOnClick(); });
});<commit_msg>France container PromotionBoxGamified updated<commit_after>function initOnClick() {

    /* This enables the click on the whole box to follow the link on the <a> with "button" class. */
    $('.PromotionBoxGamified').click(function () {
        var link = $(this).find('.button');
        window.open(link.attr('href'), link.attr('target') != null ? link.attr('target') : '_self');
        return false;
    });
}

$(document).ready(function () {
    initOnClick();

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () { initOnClick(); });
});<|endoftext|>


<commit_before>"use strict";

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange')
const { ExchangeError, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError } = require ('./base/errors')

//  ---------------------------------------------------------------------------

module.exports = class braziliex extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'braziliex',
            'name': 'Braziliex',
            'countries': 'BR',
            'rateLimit': 1000,
            // obsolete metainfo interface
            'hasFetchTickers': true,
            'hasFetchOpenOrders': true,
            'hasFetchClosedOrders': true,
            // new metainfo interface
            'has': {
                'fetchTickers': true,
                'fetchOpenOrders': true,
                'fetchClosedOrders': true,
            },
            'urls': {
                'logo': 'https://braziliex.com/img/logo_topo.png',
                'api': 'https://braziliex.com/api/v1',
                'www': 'https://braziliex.com/',
                'doc': 'https://braziliex.com/exchange/api.php',
                'fees': 'https://braziliex.com/exchange/fees.php',
            },
            'api': {
                'public': {
                    'get': [
                        'currencies',
                        'ticker',
                        'ticker/{market}',
                        'orderbook/{market}',
                        'tradehistory/{market}',
                    ],
                },
                'private': {
                    'post': [
                        'balance',
                        'complete_balance',
                        'open_orders',
                        'trade_history',
                        'deposit_address',
                        'sell',
                        'buy',
                        'cancel_order',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.005,
                    'taker': 0.005,
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
        });
    }

    async fetchCurrencies (params = {}) {
        let currencies = await this.publicGetCurrencies (params);
        let ids = Object.keys (currencies);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let currency = currencies[id];
            let precision = currency['decimal'];
            let idUpperCase = id.toUpperCase ();
            let code = this.commonCurrencyCode (idUpperCase);
            let active = currency['active'] == 1;
            let status = 'ok';
            if (currency['under_maintenance'] != 0) {
                active = false;
                status = 'maintenance';
            }
            let canWithdraw = currency['is_withdrawal_active'] == 1;
            let canDeposit = currency['is_deposit_active'] == 1;
            result[code] = {
                'id': id,
                'code': code,
                'name': currency['name'],
                'active': active,
                'status': status,
                'precision': precision,
                'wallet': {
                    'address': undefined,
                    'extra': undefined,
                    'withdraw': {
                        'active': canWithdraw,
                        'fee': currency['txWithdrawalFee'],
                    },
                    'deposit': {
                        'active': canDeposit,
                        'fee': currency['txDepositFee'],
                    },
                },
                'limits': {
                    'amount': {
                        'min': currency['minAmountTrade'],
                        'max': Math.pow (10, precision),
                    },
                    'price': {
                        'min': Math.pow (10, -precision),
                        'max': Math.pow (10, precision),
                    },
                    'cost': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': currency['MinWithdrawal'],
                        'max': Math.pow (10, precision),
                    },
                    'deposit': {
                        'min': currency['minDeposit'],
                        'max': undefined,
                    },
                },
                'info': currency,
            };
        }
        return result;
    }

    async fetchMarkets () {
        let markets = await this.publicGetTicker ();
        let ids = Object.keys (markets);
        let result = [];
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = markets[id];
            let idUpperCase = id.toUpperCase ();
            let [ base, quote ] = idUpperCase.split ('_');
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let active = market['active'] == 1;
            result.push ({
                'id': id,
                'symbol': symbol.toUpperCase (),
                'base': base,
                'quote': quote,
                'active': active,
                'info': market,
            });
        }
        return result;
    }

    parseTicker (ticker, market = undefined) {
        let symbol = market['symbol'];
        let timestamp = ticker['date'];
        ticker = ticker['ticker'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['highestBid24']),
            'low': parseFloat (ticker['lowestAsk24']),
            'bid': parseFloat (ticker['highestBid']),
            'ask': parseFloat (ticker['lowestAsk']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last']),
            'change': parseFloat (ticker['percentChange']),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['baseVolume24']),
            'quoteVolume': parseFloat (ticker['quoteVolume24']),
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTickerMarket (this.extend ({
            'market': market['id'],
        }, params));
        ticker = {
            'date': this.milliseconds (),
            'ticker': ticker,
        };
        return this.parseTicker (ticker, market);
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTicker (params);
        let result = {};
        let timestamp = this.milliseconds ();
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = {
                'date': timestamp,
                'ticker': tickers[id],
            };
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchOrderBook (symbol, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderbookMarket (this.extend ({
            'market': this.marketId (symbol),
        }, params));
        return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'amount');
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.parse8601 (trade['date_exec']);
        let price = parseFloat (trade['price']);
        let amount = parseFloat (trade['amount']);
        let symbol = market['symbol'];
        let cost = parseFloat (trade['total']);
        return {
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': this.safeString (trade, '_id'),
            'order': undefined,
            'type': 'limit',
            'side': trade['type'],
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let trades = await this.publicGetTradehistoryMarket (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balances = await this.privatePostCompleteBalance (params);
        let result = { 'info': balances };
        let currencies = Object.keys (balances);
        for (let i = 0; i < currencies.length; i++) {
            let id = currencies[i];
            let balance = balances[id];
            let currency = this.commonCurrencyCode (id);
            let account = {
                'free': parseFloat (balance['available']),
                'used': 0.0,
                'total': parseFloat (balance['total']),
            };
            account['used'] = account['total'] - account['free'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    parseOrder (order, market, status = 'open') {
        let symbol = market['symbol'];
        let timestamp = this.parse8601 (order['date']);
        let price = parseFloat (order['price']);
        let cost = this.safeFloat (order, 'total', 0.0);
        let amount = this.safeFloat (order, 'amount');
        let filled = this.safeFloat (order, 'progress');
        let remaining = this.amountToPrecision (symbol, amount - filled);
        let info = order;
        if ('info' in info)
            info = order['info'];
        return {
            'id': order['order_number'],
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'status': status,
            'symbol': symbol,
            'type': 'limit',
            'side': order['type'],
            'price': price,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': undefined,
            'fee': undefined,
            'info': info,
        };
    }

    parseOrders (orders, market, status = undefined) {
        let result = [];
        for (let i = 0; i < orders.length; i++) {
            let order = orders[i];
            result.push (this.parseOrder (order, market, status));
        }
        return result;
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let method = 'privatePost' + this.capitalize (side.toLowerCase ());
        let response = await this[method] (this.extend ({
            'market': market['id'],
            'price': this.priceToPrecision (symbol, price),
            'amount': this.amountToPrecision (symbol, amount),
        }, params));
        if (this.safeInteger(response, 'success') != 1)
            throw new InvalidOrder (this.id + ' ' + this.json (response));
        let chunks = response['message'].split (' / ').slice (1);
        let order = this.parseOrder ({
            'order_number': response['order_number'],
            'type': chunks[0].toLowerCase (),
            'market': chunks[1].toLowerCase (),
            'amount': chunks[2].split (' ')[1],
            'price': chunks[3].split (' ')[1],
            'total': chunks[4].split (' ')[1],
            'progress': 0.0,
            'date': this.iso8601 (this.milliseconds ()),
            'info': response,
        }, market);
        let id = order['id'];
        this.orders[id] = order;
        return order;
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let result = await this.privatePostCancelOrder (this.extend ({
            'order_number': id,
            'market': market['id'],
        }, params));
        return result;
    }

    async fetchOpenOrders (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orders = await this.privatePostOpenOrders (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseOrders (orders['order_open'], market, 'open');
    }

    async fetchClosedOrders (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orders = await this.privatePostTradeHistory (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseOrders (orders['trade_history'], market, 'closed');
    }

    async fetchDepositAddress (currencyCode, params = {}) {
        await this.loadMarkets ();
        let currency = this.currency (currencyCode);
        let response = await this.privatePostDepositAddress (this.extend ({
            'currency': currency['id'],
        }, params));
        let address = this.safeString (response['deposit_address'], 'address');
        if (!address)
            throw new ExchangeError (this.id + ' fetchDepositAddress failed: ' + this.last_http_response);
        return {
            'currency': currencyCode,
            'address': address,
            'status': 'ok',
            'info': response,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + api + '/';
        let query = undefined;
        if (api == 'public') {
            query = this.urlencode (this.omit (params, this.extractParams (path)));
            url += this.implodeParams (path, params);
            if (query.length)
                url += '?' + query;
        } else {
            this.checkRequiredCredentials ();
            query = this.extend ({
                'command': path,
                'nonce': this.nonce (),
            }, params);
            body = this.json (query);
            query = this.urlencode (query);
            let signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha256');
            headers = {
                'Content-type': 'application/json',
                'Key': this.apiKey,
                'Sign': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        let success = this.safeInteger (response, 'success');
        if (success == 0) {
            let message = response['message'];
            if (message == 'Invalid APIKey')
                throw new AuthenticationError (message);
            throw new ExchangeError (message);
        }
        return response;
    }
}
<commit_msg>multiple fixes for Python and PHP syntax #1117<commit_after>"use strict";

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange')
const { ExchangeError, InsufficientFunds, OrderNotFound, InvalidOrder, AuthenticationError } = require ('./base/errors')

//  ---------------------------------------------------------------------------

module.exports = class braziliex extends Exchange {

    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'braziliex',
            'name': 'Braziliex',
            'countries': 'BR',
            'rateLimit': 1000,
            // obsolete metainfo interface
            'hasFetchTickers': true,
            'hasFetchOpenOrders': true,
            'hasFetchClosedOrders': true,
            // new metainfo interface
            'has': {
                'fetchTickers': true,
                'fetchOpenOrders': true,
                'fetchClosedOrders': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/34703593-c4498674-f504-11e7-8d14-ff8e44fb78c1.jpg',
                'api': 'https://braziliex.com/api/v1',
                'www': 'https://braziliex.com/',
                'doc': 'https://braziliex.com/exchange/api.php',
                'fees': 'https://braziliex.com/exchange/fees.php',
            },
            'api': {
                'public': {
                    'get': [
                        'currencies',
                        'ticker',
                        'ticker/{market}',
                        'orderbook/{market}',
                        'tradehistory/{market}',
                    ],
                },
                'private': {
                    'post': [
                        'balance',
                        'complete_balance',
                        'open_orders',
                        'trade_history',
                        'deposit_address',
                        'sell',
                        'buy',
                        'cancel_order',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'maker': 0.005,
                    'taker': 0.005,
                },
            },
            'precision': {
                'amount': 8,
                'price': 8,
            },
        });
    }

    async fetchCurrencies (params = {}) {
        let currencies = await this.publicGetCurrencies (params);
        let ids = Object.keys (currencies);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let currency = currencies[id];
            let precision = currency['decimal'];
            let idUpperCase = id.toUpperCase ();
            let code = this.commonCurrencyCode (idUpperCase);
            let active = currency['active'] == 1;
            let status = 'ok';
            if (currency['under_maintenance'] != 0) {
                active = false;
                status = 'maintenance';
            }
            let canWithdraw = currency['is_withdrawal_active'] == 1;
            let canDeposit = currency['is_deposit_active'] == 1;
            result[code] = {
                'id': id,
                'code': code,
                'name': currency['name'],
                'active': active,
                'status': status,
                'precision': precision,
                'wallet': {
                    'address': undefined,
                    'extra': undefined,
                    'withdraw': {
                        'active': canWithdraw,
                        'fee': currency['txWithdrawalFee'],
                    },
                    'deposit': {
                        'active': canDeposit,
                        'fee': currency['txDepositFee'],
                    },
                },
                'limits': {
                    'amount': {
                        'min': currency['minAmountTrade'],
                        'max': Math.pow (10, precision),
                    },
                    'price': {
                        'min': Math.pow (10, -precision),
                        'max': Math.pow (10, precision),
                    },
                    'cost': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': currency['MinWithdrawal'],
                        'max': Math.pow (10, precision),
                    },
                    'deposit': {
                        'min': currency['minDeposit'],
                        'max': undefined,
                    },
                },
                'info': currency,
            };
        }
        return result;
    }

    async fetchMarkets () {
        let markets = await this.publicGetTicker ();
        let ids = Object.keys (markets);
        let result = [];
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = markets[id];
            let idUpperCase = id.toUpperCase ();
            let [ base, quote ] = idUpperCase.split ('_');
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            let active = market['active'] == 1;
            result.push ({
                'id': id,
                'symbol': symbol.toUpperCase (),
                'base': base,
                'quote': quote,
                'active': active,
                'info': market,
            });
        }
        return result;
    }

    parseTicker (ticker, market = undefined) {
        let symbol = market['symbol'];
        let timestamp = ticker['date'];
        ticker = ticker['ticker'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': parseFloat (ticker['highestBid24']),
            'low': parseFloat (ticker['lowestAsk24']),
            'bid': parseFloat (ticker['highestBid']),
            'ask': parseFloat (ticker['lowestAsk']),
            'vwap': undefined,
            'open': undefined,
            'close': undefined,
            'first': undefined,
            'last': parseFloat (ticker['last']),
            'change': parseFloat (ticker['percentChange']),
            'percentage': undefined,
            'average': undefined,
            'baseVolume': parseFloat (ticker['baseVolume24']),
            'quoteVolume': parseFloat (ticker['quoteVolume24']),
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTickerMarket (this.extend ({
            'market': market['id'],
        }, params));
        ticker = {
            'date': this.milliseconds (),
            'ticker': ticker,
        };
        return this.parseTicker (ticker, market);
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let tickers = await this.publicGetTicker (params);
        let result = {};
        let timestamp = this.milliseconds ();
        let ids = Object.keys (tickers);
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = {
                'date': timestamp,
                'ticker': tickers[id],
            };
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchOrderBook (symbol, params = {}) {
        await this.loadMarkets ();
        let orderbook = await this.publicGetOrderbookMarket (this.extend ({
            'market': this.marketId (symbol),
        }, params));
        return this.parseOrderBook (orderbook, undefined, 'bids', 'asks', 'price', 'amount');
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.parse8601 (trade['date_exec']);
        let price = parseFloat (trade['price']);
        let amount = parseFloat (trade['amount']);
        let symbol = market['symbol'];
        let cost = parseFloat (trade['total']);
        return {
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'id': this.safeString (trade, '_id'),
            'order': undefined,
            'type': 'limit',
            'side': trade['type'],
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
            'info': trade,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let trades = await this.publicGetTradehistoryMarket (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balances = await this.privatePostCompleteBalance (params);
        let result = { 'info': balances };
        let currencies = Object.keys (balances);
        for (let i = 0; i < currencies.length; i++) {
            let id = currencies[i];
            let balance = balances[id];
            let currency = this.commonCurrencyCode (id);
            let account = {
                'free': parseFloat (balance['available']),
                'used': 0.0,
                'total': parseFloat (balance['total']),
            };
            account['used'] = account['total'] - account['free'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    parseOrder (order, market, status = 'open') {
        let symbol = market['symbol'];
        let timestamp = this.parse8601 (order['date']);
        let price = parseFloat (order['price']);
        let cost = this.safeFloat (order, 'total', 0.0);
        let amount = this.safeFloat (order, 'amount');
        let filled = this.safeFloat (order, 'progress');
        let remaining = this.amountToPrecision (symbol, amount - filled);
        let info = order;
        if ('info' in info)
            info = order['info'];
        return {
            'id': order['order_number'],
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'status': status,
            'symbol': symbol,
            'type': 'limit',
            'side': order['type'],
            'price': price,
            'cost': cost,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'trades': undefined,
            'fee': undefined,
            'info': info,
        };
    }

    parseOrders (orders, market, status = undefined) {
        let result = [];
        for (let i = 0; i < orders.length; i++) {
            let order = orders[i];
            result.push (this.parseOrder (order, market, status));
        }
        return result;
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let method = 'privatePost' + this.capitalize (side);
        let response = await this[method] (this.extend ({
            'market': market['id'],
            // 'price': this.priceToPrecision (symbol, price),
            // 'amount': this.amountToPrecision (symbol, amount),
            'price': price,
            'amount': amount,
        }, params));
        if (this.safeInteger (response, 'success') != 1)
            throw new InvalidOrder (this.id + ' ' + this.json (response));
        let chunks = response['message'].split (' / ').slice (1);
        let order = this.parseOrder ({
            'order_number': response['order_number'],
            'type': chunks[0].toLowerCase (),
            'market': chunks[1].toLowerCase (),
            'amount': chunks[2].split (' ')[1],
            'price': chunks[3].split (' ')[1],
            'total': chunks[4].split (' ')[1],
            'progress': 0.0,
            'date': this.iso8601 (this.milliseconds ()),
            'info': response,
        }, market);
        let id = order['id'];
        this.orders[id] = order;
        return order;
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let result = await this.privatePostCancelOrder (this.extend ({
            'order_number': id,
            'market': market['id'],
        }, params));
        return result;
    }

    async fetchOpenOrders (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orders = await this.privatePostOpenOrders (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseOrders (orders['order_open'], market, 'open');
    }

    async fetchClosedOrders (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let orders = await this.privatePostTradeHistory (this.extend ({
            'market': market['id'],
        }, params));
        return this.parseOrders (orders['trade_history'], market, 'closed');
    }

    async fetchDepositAddress (currencyCode, params = {}) {
        await this.loadMarkets ();
        let currency = this.currency (currencyCode);
        let response = await this.privatePostDepositAddress (this.extend ({
            'currency': currency['id'],
        }, params));
        let address = this.safeString (response['deposit_address'], 'address');
        if (!address)
            throw new ExchangeError (this.id + ' fetchDepositAddress failed: ' + this.last_http_response);
        return {
            'currency': currencyCode,
            'address': address,
            'status': 'ok',
            'info': response,
        };
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + api + '/';
        let query = undefined;
        if (api == 'public') {
            query = this.urlencode (this.omit (params, this.extractParams (path)));
            url += this.implodeParams (path, params);
            if (query.length)
                url += '?' + query;
        } else {
            this.checkRequiredCredentials ();
            query = this.extend ({
                'command': path,
                'nonce': this.nonce (),
            }, params);
            body = this.json (query);
            query = this.urlencode (query);
            let signature = this.hmac (this.encode (query), this.encode (this.secret), 'sha256');
            headers = {
                'Content-type': 'application/json',
                'Key': this.apiKey,
                'Sign': signature,
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        let success = this.safeInteger (response, 'success');
        if (success == 0) {
            let message = response['message'];
            if (message == 'Invalid APIKey')
                throw new AuthenticationError (message);
            throw new ExchangeError (message);
        }
        return response;
    }
}
<|endoftext|>


<commit_before>var spinner;
var sourcesSelect;
var mangaGrid;
var scrollBox;
var searchBox;

var loginDialog;
var loginDialogTitle;
var loginDialogUsername;
var loginDialogPassword;
var loginDialogClose;
var loginDialogLogin;
var dialogSpinner;

var currentRequest = null;
var currentSources = [];

var typingTimer;
var doneTypingInterval = 250;

var scrollEndPadding = 1000;

var searchState;
function resetSearchState() {
    searchState = {
        query: null,
        lastUrl: null,
        page: 1
    };
}
resetSearchState();

function onLoad() {
    spinner = $(".loading_spinner");
    sourcesSelect = $("#sources_select");
    mangaGrid = $("#manga_grid");
    scrollBox = $("#page_wrapper");
    searchBox = $("#search_box");

    loginDialog = $("#login_dialog");
    loginDialogTitle = $("#login_dialog_title");
    loginDialogUsername = $("#login_dialog_username");
    loginDialogPassword = $("#login_dialog_password");
    loginDialogClose = $("#login_dialog_close");
    loginDialogLogin = $("#login_dialog_login");
    dialogSpinner = $("#login_loading_spinner");

    setupLoginDialog();
    setupSourcesSelect();
    setupScrollBox();
    setupSearchBox();
    refreshSources();
}

function setupLoginDialog() {
    if (!rawElement(loginDialog).showModal) {
        dialogPolyfill.registerDialog(rawElement(loginDialog));
    }
    loginDialogClose.click(function() {
        rawElement(loginDialog).close();
    });
    loginDialogLogin.click(function() {
        loginDialogClose.prop("disabled", true);
        loginDialogLogin.prop("disabled", true);
        loginDialogUsername.prop("disabled", true);
        loginDialogPassword.prop("disabled", true);
        dialogSpinner.css("display", "initial");
        var xhr = new XMLHttpRequest();
        xhr.open("GET", buildLoginUrl(), true);
        xhr.onload = function () {
            try {
                var res = JSON.parse(xhr.responseText);
                if (res.success) {
                    refreshSources();
                } else {
                    sourceLoginError(res.error);
                }
            } catch (e) {
                sourceLoginError("Unknown Javascript error");
                console.error(e);
            }
            rawElement(loginDialog).close();
        };
        xhr.onerror = function () {
            sourceLoginError("Unknown error");
            rawElement(loginDialog).close();
        };
        xhr.send();
    })
}

function buildLoginUrl() {
    return sourceLoginRoot + "/" + rawElement(sourcesSelect).value + "?username=" + encodeURIComponent(loginDialogUsername.val()) + "&password=" + encodeURIComponent(loginDialogPassword);
}

function setupSearchBox() {
    /** http://stackoverflow.com/questions/4220126/run-javascript-function-when-user-finishes-typing-instead-of-on-key-up **/
    searchBox.on("keyup", function() {
        clearTimeout(typingTimer);
        typingTimer = setTimeout(performSearch, doneTypingInterval);
    });
    searchBox.on("keydown", function() {
        clearTimeout(typingTimer);
    });
}

function performSearch() {
    searchState.page = 1;
    searchState.lastUrl = null;
    var searchText = rawElement(searchBox).value;
    if(searchText.trim() !== "") {
        searchState.query = searchText;
    } else {
        //Set to null if no query
        searchState.query = null;
    }
    scrollBox.scrollTop(0);
    clearElement(mangaGrid);
    refreshCatalogue();
}

function setupScrollBox() {
    scrollBox.on('scroll', function() {
        if(scrollBox.scrollTop() + scrollBox.innerHeight() >= rawElement(scrollBox).scrollHeight - scrollEndPadding) {
            if(hasNextPage() && !isRefreshing()) {
                searchState.page++;
                refreshCatalogue();
            }
        }
    });
}

function setupSourcesSelect() {
    sourcesSelect.change(function() {
        var selectedSource = getCurrentSource();
        if(valid(selectedSource) && !selectedSource["logged_in"]) {
            showLoginBox(selectedSource);
        } else {
            searchState.page = 1;
            searchState.lastUrl = null;
            scrollBox.scrollTop(0);
            clearElement(mangaGrid);
            refreshCatalogue();
        }
    });
}

function showLoginBox(source) {
    loginDialogTitle.text(source.name + " Login");
    loginDialogUsername.val("");
    loginDialogPassword.val("");
    loginDialogClose.prop('disabled', false);
    loginDialogLogin.prop('disabled', false);
    loginDialogUsername.prop("disabled", false);
    loginDialogPassword.prop("disabled", false);
    loginDialog.data("source", source);
    dialogSpinner.css("display", "");
    rawElement(loginDialog).showModal();
}

function getCurrentSource() {
    var currentSourceID = parseInt(rawElement(sourcesSelect).value);
    for(var i = 0; i < currentSources.length; i++) {
        var currentSource = currentSources[i];
        if(currentSource.id === currentSourceID) {
            return currentSource;
        }
    }
    return null;
}

function showSpinner() {
    spinner.css("opacity", 1);
}

function hideSpinner() {
    spinner.css("opacity", 0);
}

function refreshSources() {
    showSpinner();
    var xhr = new XMLHttpRequest();
    xhr.open("GET", sourcesRoot, true);
    xhr.onload = function () {
        try {
            var res = JSON.parse(xhr.responseText);
            if (res.success) {
                currentSources = res.content;
                updateSourcesUI();
                refreshCatalogue();
            } else {
                sourcesRefreshError();
            }
        } catch (e) {
            console.error(e);
            sourcesRefreshError();
        }
        hideSpinner();
    };
    xhr.onerror = function () {
        sourcesRefreshError();
        hideSpinner();
    };
    xhr.send();
}

function buildCatalogueURL() {
    var currentUrl = catalogueRoot + "/" + rawElement(sourcesSelect).value + "/" + searchState.page;
    var usedQuestionMark = false;
    if(valid(searchState.lastUrl)) {
        currentUrl += usedQuestionMark ? "&" : "?";
        currentUrl += "lurl=" + encodeURIComponent(searchState.lastUrl);
        usedQuestionMark = true;
    }
    if(valid(searchState.query)) {
        currentUrl += usedQuestionMark ? "&" : "?";
        currentUrl += "query=" + encodeURIComponent(searchState.query);
    }
    return currentUrl;
}
function isRefreshing() {
    return valid(currentRequest) && !currentRequest.completed;
}

function hasNextPage() {
    return !!(valid(searchState.lastUrl) && searchState.lastUrl !== "");
}

//TODO Pages and search
function refreshCatalogue(oldRequest) {
    var request;
    if (valid(oldRequest)) {
        request = oldRequest;
    } else {
        request = new Request();
    }
    if(currentRequest === request) {
        currentRequest.cancel();
    }
    request.completed = false;
    currentRequest = request;
    showSpinner();
    var xhr = new XMLHttpRequest();
    xhr.open("GET", buildCatalogueURL(), true);
    xhr.onload = function () {
        if (request.canceled) {
            return;
        }
        try {
            var res = JSON.parse(xhr.responseText);
            if(res.success) {
                //Clear catalogue if page 1
                if(searchState.page === 1) {
                    clearElement(mangaGrid);
                }
                //Set new search state
                searchState.lastUrl = res.lurl;
                //Add on new manga
                updateCatalogueUI(res.content);
            } else {
                catalogueRefreshError(request);
            }
        }
        catch (e) {
            console.error(e);
            catalogueRefreshError(request);
        }
        hideSpinner();
        request.completed = true;
    };
    xhr.onerror = function () {
        if (request.canceled) {
            return;
        }
        catalogueRefreshError(request);
        hideSpinner();
        request.completed = true;
    };
    xhr.send();
    return request;
}

function updateSourcesUI() {
    clearElement(sourcesSelect);
    $.each(currentSources, function (index, value) {
        sourcesSelect.append($('<option/>', {
            value: value.id,
            text : value.name
        }));
    });
}

function updateCatalogueUI(manga) {
    $.each(manga, function (index, value) {
        appendManga(value, mangaGrid, true);
    });
}

/**
 * Basic cancelable request
 **/
function Request() {
    var that = this;
    this.canceled = false;
    this.cancel = function () {
        that.canceled = true;
    };
    this.completed = false;
}

function sourcesRefreshError() {
    snackbar.showSnackbar({
        message: "Error getting sources!",
        timeout: 2000,
        actionText: "Retry",
        actionHandler: function () {
            refreshSources();
        }
    });
}

function catalogueRefreshError(request) {
    snackbar.showSnackbar({
        message: "Error getting catalogue!",
        timeout: 2000,
        actionText: "Retry",
        actionHandler: function () {
            refreshCatalogue(request);
        }
    });
}

function sourceLoginError(message) {
    snackbar.showSnackbar({
        message: "Error logging in (" + message + ")!",
        timeout: 2000
    });
}<commit_msg>Fix login dialog showing on sources that do not require login.<commit_after>var spinner;
var sourcesSelect;
var mangaGrid;
var scrollBox;
var searchBox;

var loginDialog;
var loginDialogTitle;
var loginDialogUsername;
var loginDialogPassword;
var loginDialogClose;
var loginDialogLogin;
var dialogSpinner;

var currentRequest = null;
var currentSources = [];

var typingTimer;
var doneTypingInterval = 250;

var scrollEndPadding = 1000;

var searchState;
function resetSearchState() {
    searchState = {
        query: null,
        lastUrl: null,
        page: 1
    };
}
resetSearchState();

function onLoad() {
    spinner = $(".loading_spinner");
    sourcesSelect = $("#sources_select");
    mangaGrid = $("#manga_grid");
    scrollBox = $("#page_wrapper");
    searchBox = $("#search_box");

    loginDialog = $("#login_dialog");
    loginDialogTitle = $("#login_dialog_title");
    loginDialogUsername = $("#login_dialog_username");
    loginDialogPassword = $("#login_dialog_password");
    loginDialogClose = $("#login_dialog_close");
    loginDialogLogin = $("#login_dialog_login");
    dialogSpinner = $("#login_loading_spinner");

    setupLoginDialog();
    setupSourcesSelect();
    setupScrollBox();
    setupSearchBox();
    refreshSources();
}

function setupLoginDialog() {
    if (!rawElement(loginDialog).showModal) {
        dialogPolyfill.registerDialog(rawElement(loginDialog));
    }
    loginDialogClose.click(function() {
        rawElement(loginDialog).close();
    });
    loginDialogLogin.click(function() {
        loginDialogClose.prop("disabled", true);
        loginDialogLogin.prop("disabled", true);
        loginDialogUsername.prop("disabled", true);
        loginDialogPassword.prop("disabled", true);
        dialogSpinner.css("display", "initial");
        var xhr = new XMLHttpRequest();
        xhr.open("GET", buildLoginUrl(), true);
        xhr.onload = function () {
            try {
                var res = JSON.parse(xhr.responseText);
                if (res.success) {
                    refreshSources();
                } else {
                    sourceLoginError(res.error);
                }
            } catch (e) {
                sourceLoginError("Unknown Javascript error");
                console.error(e);
            }
            rawElement(loginDialog).close();
        };
        xhr.onerror = function () {
            sourceLoginError("Unknown error");
            rawElement(loginDialog).close();
        };
        xhr.send();
    })
}

function buildLoginUrl() {
    return sourceLoginRoot + "/" + rawElement(sourcesSelect).value + "?username=" + encodeURIComponent(loginDialogUsername.val()) + "&password=" + encodeURIComponent(loginDialogPassword);
}

function setupSearchBox() {
    /** http://stackoverflow.com/questions/4220126/run-javascript-function-when-user-finishes-typing-instead-of-on-key-up **/
    searchBox.on("keyup", function() {
        clearTimeout(typingTimer);
        typingTimer = setTimeout(performSearch, doneTypingInterval);
    });
    searchBox.on("keydown", function() {
        clearTimeout(typingTimer);
    });
}

function performSearch() {
    searchState.page = 1;
    searchState.lastUrl = null;
    var searchText = rawElement(searchBox).value;
    if(searchText.trim() !== "") {
        searchState.query = searchText;
    } else {
        //Set to null if no query
        searchState.query = null;
    }
    scrollBox.scrollTop(0);
    clearElement(mangaGrid);
    refreshCatalogue();
}

function setupScrollBox() {
    scrollBox.on('scroll', function() {
        if(scrollBox.scrollTop() + scrollBox.innerHeight() >= rawElement(scrollBox).scrollHeight - scrollEndPadding) {
            if(hasNextPage() && !isRefreshing()) {
                searchState.page++;
                refreshCatalogue();
            }
        }
    });
}

function setupSourcesSelect() {
    sourcesSelect.change(function() {
        var selectedSource = getCurrentSource();
        if(valid(selectedSource) && valid(selectedSource["logged_in"]) && !selectedSource["logged_in"]) {
            showLoginBox(selectedSource);
        } else {
            searchState.page = 1;
            searchState.lastUrl = null;
            scrollBox.scrollTop(0);
            clearElement(mangaGrid);
            refreshCatalogue();
        }
    });
}

function showLoginBox(source) {
    loginDialogTitle.text(source.name + " Login");
    loginDialogUsername.val("");
    loginDialogPassword.val("");
    loginDialogClose.prop('disabled', false);
    loginDialogLogin.prop('disabled', false);
    loginDialogUsername.prop("disabled", false);
    loginDialogPassword.prop("disabled", false);
    loginDialog.data("source", source);
    dialogSpinner.css("display", "");
    rawElement(loginDialog).showModal();
}

function getCurrentSource() {
    var currentSourceID = parseInt(rawElement(sourcesSelect).value);
    for(var i = 0; i < currentSources.length; i++) {
        var currentSource = currentSources[i];
        if(currentSource.id === currentSourceID) {
            return currentSource;
        }
    }
    return null;
}

function showSpinner() {
    spinner.css("opacity", 1);
}

function hideSpinner() {
    spinner.css("opacity", 0);
}

function refreshSources() {
    showSpinner();
    var xhr = new XMLHttpRequest();
    xhr.open("GET", sourcesRoot, true);
    xhr.onload = function () {
        try {
            var res = JSON.parse(xhr.responseText);
            if (res.success) {
                currentSources = res.content;
                updateSourcesUI();
                refreshCatalogue();
            } else {
                sourcesRefreshError();
            }
        } catch (e) {
            console.error(e);
            sourcesRefreshError();
        }
        hideSpinner();
    };
    xhr.onerror = function () {
        sourcesRefreshError();
        hideSpinner();
    };
    xhr.send();
}

function buildCatalogueURL() {
    var currentUrl = catalogueRoot + "/" + rawElement(sourcesSelect).value + "/" + searchState.page;
    var usedQuestionMark = false;
    if(valid(searchState.lastUrl)) {
        currentUrl += usedQuestionMark ? "&" : "?";
        currentUrl += "lurl=" + encodeURIComponent(searchState.lastUrl);
        usedQuestionMark = true;
    }
    if(valid(searchState.query)) {
        currentUrl += usedQuestionMark ? "&" : "?";
        currentUrl += "query=" + encodeURIComponent(searchState.query);
    }
    return currentUrl;
}
function isRefreshing() {
    return valid(currentRequest) && !currentRequest.completed;
}

function hasNextPage() {
    return !!(valid(searchState.lastUrl) && searchState.lastUrl !== "");
}

//TODO Pages and search
function refreshCatalogue(oldRequest) {
    var request;
    if (valid(oldRequest)) {
        request = oldRequest;
    } else {
        request = new Request();
    }
    if(currentRequest === request) {
        currentRequest.cancel();
    }
    request.completed = false;
    currentRequest = request;
    showSpinner();
    var xhr = new XMLHttpRequest();
    xhr.open("GET", buildCatalogueURL(), true);
    xhr.onload = function () {
        if (request.canceled) {
            return;
        }
        try {
            var res = JSON.parse(xhr.responseText);
            if(res.success) {
                //Clear catalogue if page 1
                if(searchState.page === 1) {
                    clearElement(mangaGrid);
                }
                //Set new search state
                searchState.lastUrl = res.lurl;
                //Add on new manga
                updateCatalogueUI(res.content);
            } else {
                catalogueRefreshError(request);
            }
        }
        catch (e) {
            console.error(e);
            catalogueRefreshError(request);
        }
        hideSpinner();
        request.completed = true;
    };
    xhr.onerror = function () {
        if (request.canceled) {
            return;
        }
        catalogueRefreshError(request);
        hideSpinner();
        request.completed = true;
    };
    xhr.send();
    return request;
}

function updateSourcesUI() {
    clearElement(sourcesSelect);
    $.each(currentSources, function (index, value) {
        sourcesSelect.append($('<option/>', {
            value: value.id,
            text : value.name
        }));
    });
}

function updateCatalogueUI(manga) {
    $.each(manga, function (index, value) {
        appendManga(value, mangaGrid, true);
    });
}

/**
 * Basic cancelable request
 **/
function Request() {
    var that = this;
    this.canceled = false;
    this.cancel = function () {
        that.canceled = true;
    };
    this.completed = false;
}

function sourcesRefreshError() {
    snackbar.showSnackbar({
        message: "Error getting sources!",
        timeout: 2000,
        actionText: "Retry",
        actionHandler: function () {
            refreshSources();
        }
    });
}

function catalogueRefreshError(request) {
    snackbar.showSnackbar({
        message: "Error getting catalogue!",
        timeout: 2000,
        actionText: "Retry",
        actionHandler: function () {
            refreshCatalogue(request);
        }
    });
}

function sourceLoginError(message) {
    snackbar.showSnackbar({
        message: "Error logging in (" + message + ")!",
        timeout: 2000
    });
}<|endoftext|>


<commit_before>// Functions
/// write_post:   Write a post, returns an ID (string).
/// read_post:    Read a post, returns a JSON object (teacher, title, text, club, picture)
/// update_post:  Update a post, returns a boolean, whether it was successful or not.
/// display_post: Display a post in the HTML element given by the second argument, returns null.
function write_post(title, text, club, picture) {
  key = firebase.database().ref().child('posts').push().key;
  firebase.database().ref('users/' + firebase.auth().currentUser.uid).once('value').then(function(snapshot) {
    name = snapshot.val().display
  });
  firebase.database().ref('posts/' + key).set({
    teacher: name || 'Anonymous',
    title: title,
    text: text,
    club: club || 'School',
    picture: picture || null
  });
  return key;
}

function read_post(key) {
  firebase.database().ref('posts/' + key).once('value').then(function(snapshot) {
    post = snapshot.val();
    //console.log(post);
  });
  //console.log(post);
  return post;
}

function update_post(key, title, text, club, picture) {
  reference = read_post(key);
  if (reference == null) {
    return false;
  } else {
    firebase.database().ref('users/' + firebase.auth().currentUser.uid).once('value').then(function(snapshot) {
      name = snapshot.val().display
    });
    firebase.database().ref('posts/' + key).update({
      teacher: name || reference.teacher,
      title: title || reference.title,
      text: text || reference.text,
      club: club || reference.club,
      picture: picture || reference.picture,
    })
    return true;
  }
}

function display_post(key, parent) {
  post = read_post(key);
  console.log(post);
  div = document.createElement('div');
  div.innerHTML = '
' +
  '
' + post.club + ' / ' + post.teacher + '
';
  if (post.picture !== null) {
    div.innerHTML += '';
  }
  div.innerHTML += '
' +
  '
' + post.title + '
' +
  '
' + post.text + '
' +
  '
' +
  '
';
  parent.appendChild(div);
  return null;
}

function remove_post(key) {
  firebase.database().ref('posts/' + key).update(null);
}
<commit_msg>Introduced public variables for certain things.<commit_after>// Functions
/// write_post:   Write a post, returns an ID (string).
/// read_post:    Read a post, returns a JSON object (teacher, title, text, club, picture)
/// update_post:  Update a post, returns a boolean, whether it was successful or not.
/// display_post: Display a post in the HTML element given by the second argument, returns null.

var post;
var key;
var err;

function write_post(title, text, club, picture) {
  key = firebase.database().ref().child('posts').push().key;
  firebase.database().ref('users/' + firebase.auth().currentUser.uid).once('value').then(function(snapshot) {
    name = snapshot.val().display
  });
  firebase.database().ref('posts/' + key).set({
    teacher: name || 'Anonymous',
    title: title,
    text: text,
    club: club || 'School',
    picture: picture || null
  });
  return key;
}

function read_post(key) {
  firebase.database().ref('posts/' + key).once('value').then(function(snapshot) {
    post = snapshot.val();
    //console.log(post);
  });
  //console.log(post);
  return post;
}

function update_post(key, title, text, club, picture) {
  reference = read_post(key);
  if (reference == null) {
    return false;
  } else {
    firebase.database().ref('users/' + firebase.auth().currentUser.uid).once('value').then(function(snapshot) {
      name = snapshot.val().display
    });
    firebase.database().ref('posts/' + key).update({
      teacher: name || reference.teacher,
      title: title || reference.title,
      text: text || reference.text,
      club: club || reference.club,
      picture: picture || reference.picture,
    })
    return true;
  }
}

function display_post(key, parent) {
  post = read_post(key);
  console.log(post);
  div = document.createElement('div');
  div.innerHTML = '
' +
  '
' + post.club + ' / ' + post.teacher + '
';
  if (post.picture !== null) {
    div.innerHTML += '';
  }
  div.innerHTML += '
' +
  '
' + post.title + '
' +
  '
' + post.text + '
' +
  '
' +
  '
';
  parent.appendChild(div);
  return null;
}

function remove_post(key) {
  firebase.database().ref('posts/' + key).update(null);
}
<|endoftext|>


<commit_before>define(["require", "js/core/Component", "underscore", "moment", "flow"], function (require, Component, _, moment, flow) {
    return Component.inherit("js.core.I18n", {
        defaults: {
            path: 'app/locale',
            locale: null,
            suffix: '.json',
            translations: {},
            loadMomentJs: true
        },

        initialize: function () {
            this.callBase();
            this.loadLocale(this.$.locale);
        },

        _commitChangedAttributes: function (attributes) {
            if (attributes.locale) {
                this.loadLocale(attributes.locale);
            }
        },

        loadLocale: function (locale, callback) {

            var self = this;

            if (!locale) {
                throw "locale not defined";
            }

            flow()
                .par(function (cb) {
                    require(['json!' + self.$.path + '/' + self.$.locale], function (translations) {
                        self.set({
                            translations: translations
                        });
                        cb();
                    }, function(err) {
                        self.log(err, 'error');
                        cb();
                    });
                }, function (cb) {
                    if (self.$.loadMomentJs) {
                        require([self.$.path + "/" + self.$.locale], function () {
                            self.trigger("localeChanged");
                            cb();
                        }, function (err) {
                            self.log(err, 'error');
                            cb();
                        })
                    } else {
                        cb();
                    }
                })
                .exec(callback)

        },

        /**
         * @param [num] for plural or singular
         * @param key translation key
         * @param - replacement for %0
         * @param - replacement for %1 ...
         */
        t: function () {

            var args = Array.prototype.slice.call(arguments);
            var key = args.shift(), isPlural;
            if (_.isNumber(key)) {
                isPlural = key !== 1;
                key = args.shift();
            }
            if (isPlural) {
                key += "_plural";
            }

            var value = this.$.translations[key] || this.get(this.$.translations, key) || "";

            for (var i = 0; i < args.length; i++) {
                // replace, placeholder
                value = value.split("%" + i).join(args[i]);
            }

            return value;
        }.onChange("translations"),

        f: function (value, format) {

            if (value instanceof Date) {
                format = format || "LLL";
                return moment(value).format(format);
            } else if (value instanceof moment) {
                format = format || "LLL";
                return value.format(format);
            }

            return value;
        }.on("localeChanged")
    })
});<commit_msg>added method ts to search within a specified area<commit_after>define(["require", "js/core/Component", "underscore", "moment", "flow"], function (require, Component, _, moment, flow) {
    return Component.inherit("js.core.I18n", {
        defaults: {
            path: 'app/locale',
            locale: null,
            suffix: '.json',
            translations: {},
            loadMomentJs: true
        },

        initialize: function () {
            this.callBase();
            this.loadLocale(this.$.locale);
        },

        _commitChangedAttributes: function (attributes) {
            if (attributes.locale) {
                this.loadLocale(attributes.locale);
            }
        },

        loadLocale: function (locale, callback) {

            var self = this;

            if (!locale) {
                throw "locale not defined";
            }

            flow()
                .par(function (cb) {
                    require(['json!' + self.$.path + '/' + self.$.locale], function (translations) {
                        self.set({
                            translations: translations
                        });
                        cb();
                    }, function(err) {
                        self.log(err, 'error');
                        cb();
                    });
                }, function (cb) {
                    if (self.$.loadMomentJs) {
                        require([self.$.path + "/" + self.$.locale], function () {
                            self.trigger("localeChanged");
                            cb();
                        }, function (err) {
                            self.log(err, 'error');
                            cb();
                        })
                    } else {
                        cb();
                    }
                })
                .exec(callback)

        },

        /**
         * @param [num] for plural or singular
         * @param key translation key
         * @param - replacement for %0
         * @param - replacement for %1 ...
         */
        t: function () {

            var args = Array.prototype.slice.call(arguments);
            var key = args.shift(), isPlural;
            if (_.isNumber(key)) {
                isPlural = (key !== 1);
                key = args.shift();
            }
            if (isPlural) {
                key += "_plural";
            }

            var value = this.$.translations[key] || this.get(this.$.translations, key) || "";

            for (var i = 0; i < args.length; i++) {
                // replace, placeholder
                value = value.split("%" + i).join(args[i]);
            }

            return value;
        }.onChange("translations"),

        ts: function() {
            var args = Array.prototype.slice.call(arguments),
                newArgs = [],
                key = args.shift(),
                num;

            if (_.isNumber(key)) {
                num = key;
                key = args.shift();
            }

            // key = scope + "." + key
            key += "." + args.shift();

            newArgs = num ? [num] : [];
            newArgs.push(key);
            newArgs = newArgs.concat(args);

            return this.t.apply(this, newArgs);

        }.onChange("translations"),

        f: function (value, format) {

            if (value instanceof Date) {
                format = format || "LLL";
                return moment(value).format(format);
            } else if (value instanceof moment) {
                format = format || "LLL";
                return value.format(format);
            }

            return value;
        }.on("localeChanged")
    })
});<|endoftext|>


<commit_before>/**
 Copyright 2014 Gordon Williams (gw@pur3.co.uk)

 This Source Code is subject to the terms of the Mozilla Public
 License, v2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 ------------------------------------------------------------------
  Handling the getting and setting of code
 ------------------------------------------------------------------
**/
"use strict";
(function(){

  var viewModeButton;

  function init() {
    // Configuration
    Espruino.Core.Config.add("AUTO_SAVE_CODE", {
      section : "Communications",
      name : "Auto Save",
      description : "Save code to Chrome's cloud storage when clicking 'Send to Espruino'?",
      type : "boolean",
      defaultValue : true,
    });

    // Setup code mode button
    viewModeButton = Espruino.Core.App.addIcon({
      id: "code",
      icon: "code",
      title : "Switch between Code and Graphical Designer",
      order: 0,
      area: {
        name: "code",
        position: "bottom"
      },
      click: function() {
        if (isInBlockly()) {
          switchToCode();
          Espruino.Core.EditorJavaScript.madeVisible();
        } else {
          switchToBlockly();
        }
      }
    });

    // get code from our config area at bootup
    Espruino.addProcessor("initialised", function(data,callback) {
      var code;
      if (Espruino.Config.CODE) {
        code = Espruino.Config.CODE;
        console.log("Loaded code from storage.");
      } else {
        code = "var  on = false;\nsetInterval(function() {\n  on = !on;\n  LED1.write(on);\n}, 500);";
        console.log("No code in storage.");
      }
      Espruino.Core.EditorJavaScript.setCode(code);
      callback(data);
    });

    Espruino.addProcessor("sending", function(data, callback) {
      if(Espruino.Config.AUTO_SAVE_CODE)
        Espruino.Config.set("CODE", Espruino.Core.EditorJavaScript.getCode()); // save the code
      callback(data);
    });
  }

  function isInBlockly() { // TODO: we should really enumerate views - we might want another view?
    return $("#divblockly").is(":visible");
  };

  function switchToBlockly() {
    $("#divcode").hide();
    $("#divblockly").show();
    viewModeButton.setIcon("block");
    // Hack around issues Blockly have if we initialise when the window isn't visible
    Espruino.Core.EditorBlockly.setVisible();
  }

  function switchToCode() {
    $("#divblockly").hide();
    $("#divcode").show();
    viewModeButton.setIcon("code");
  }

  function getEspruinoCode(callback) {
    Espruino.callProcessor("transformForEspruino", getCurrentCode(), callback);
  }

  function getCurrentCode() {
    if (isInBlockly()) {
      return Espruino.Core.EditorBlockly.getCode();
    } else {
      return Espruino.Core.EditorJavaScript.getCode();
    }
  }

  Espruino.Core.Code = {
    init : init,
    getEspruinoCode : getEspruinoCode, // get the currently selected bit of code ready to send to Espruino (including Modules)
    getCurrentCode : getCurrentCode, // get the currently selected bit of code (either blockly or javascript editor)
    isInBlockly: isInBlockly,
    switchToCode: switchToCode,
    switchToBlockly: switchToBlockly
  };
}());
<commit_msg>Try and auto-save code<commit_after>/**
 Copyright 2014 Gordon Williams (gw@pur3.co.uk)

 This Source Code is subject to the terms of the Mozilla Public
 License, v2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 ------------------------------------------------------------------
  Handling the getting and setting of code
 ------------------------------------------------------------------
**/
"use strict";
(function(){

  var viewModeButton;

  function init() {
    // Configuration
    Espruino.Core.Config.add("AUTO_SAVE_CODE", {
      section : "Communications",
      name : "Auto Save",
      description : "Save code to Chrome's cloud storage when clicking 'Send to Espruino'?",
      type : "boolean",
      defaultValue : true,
    });

    // Setup code mode button
    viewModeButton = Espruino.Core.App.addIcon({
      id: "code",
      icon: "code",
      title : "Switch between Code and Graphical Designer",
      order: 0,
      area: {
        name: "code",
        position: "bottom"
      },
      click: function() {
        if (isInBlockly()) {
          switchToCode();
          Espruino.Core.EditorJavaScript.madeVisible();
        } else {
          switchToBlockly();
        }
      }
    });

    // get code from our config area at bootup
    Espruino.addProcessor("initialised", function(data,callback) {
      var code;
      if (Espruino.Config.CODE) {
        code = Espruino.Config.CODE;
        console.log("Loaded code from storage.");
      } else {
        code = "var  on = false;\nsetInterval(function() {\n  on = !on;\n  LED1.write(on);\n}, 500);";
        console.log("No code in storage.");
      }
      Espruino.Core.EditorJavaScript.setCode(code);
      callback(data);
    });

    Espruino.addProcessor("sending", function(data, callback) {
      if(Espruino.Config.AUTO_SAVE_CODE)
        Espruino.Config.set("CODE", Espruino.Core.EditorJavaScript.getCode()); // save the code
      callback(data);
    });
    // try and save code when window closes
    function saveCode(e) {
      if(Espruino.Config.AUTO_SAVE_CODE)
        Espruino.Config.set("CODE", Espruino.Core.EditorJavaScript.getCode());
    }
    window.addEventListener("close", saveCode);
    if (!Espruino.Core.Utils.isChromeWebApp()) // chrome complains if we use this
      window.addEventListener("beforeunload", saveCode);
  }

  function isInBlockly() { // TODO: we should really enumerate views - we might want another view?
    return $("#divblockly").is(":visible");
  };

  function switchToBlockly() {
    $("#divcode").hide();
    $("#divblockly").show();
    viewModeButton.setIcon("block");
    // Hack around issues Blockly have if we initialise when the window isn't visible
    Espruino.Core.EditorBlockly.setVisible();
  }

  function switchToCode() {
    $("#divblockly").hide();
    $("#divcode").show();
    viewModeButton.setIcon("code");
  }

  function getEspruinoCode(callback) {
    Espruino.callProcessor("transformForEspruino", getCurrentCode(), callback);
  }

  function getCurrentCode() {
    if (isInBlockly()) {
      return Espruino.Core.EditorBlockly.getCode();
    } else {
      return Espruino.Core.EditorJavaScript.getCode();
    }
  }

  Espruino.Core.Code = {
    init : init,
    getEspruinoCode : getEspruinoCode, // get the currently selected bit of code ready to send to Espruino (including Modules)
    getCurrentCode : getCurrentCode, // get the currently selected bit of code (either blockly or javascript editor)
    isInBlockly: isInBlockly,
    switchToCode: switchToCode,
    switchToBlockly: switchToBlockly
  };
}());
<|endoftext|>