<commit_before>require([
    'splunkjs/ready!',
    'splunkjs/mvc/simplexml/ready!',
    'underscore',
    '../app/pas_ref_app/components/kvstore_backbone/kvstore',
    'splunkjs/mvc/multidropdownview'
], function(mvc, ignored, _, KVStore, MultiDropdownView) {
    // TODO: Add error handling for I/O errors.
    //       No time to fix now since feature freeze in a few hours...

    var GOOGLE_SIGN_IN_BASE_URL = "https://accounts.google.com/o/oauth2/auth?redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fadmin.reports.audit.readonly&client_id=";

    var logService = mvc.createService();
    var currentUser = Splunk.util.getConfigValue("USERNAME");
    var dev_debug_key = "";
    var ux_logging_key = "";

    var VIOLATION_TYPE_ROW_TEMPLATE =
        _.template(
            "
"+
                "Name: <input type=\"text\" disabled/> " +
                "Color: <input type=\"text\"/ disabled> " +
                "Weight: <input type=\"text\"/>" +
            "
");

    var DEFAULT_VIOLATION_TYPES = [
        {
            id: 'Invalid_Time_Access',
            title: 'Off-Hours Access',
            color: 'Yellow',
            weight: 1.2
        },
        {
            id: 'Terminated_Access',
            title: 'Terminated Employee Access',
            color: 'Red',
            weight: 3.0
        },
        {
            id: 'Excessive_Access',
            title: 'Users with Excessive Accesses',
            color: 'Yellow',
            weight: 1.0
        }
    ];

    var departmentsDropdown = new MultiDropdownView({
        managerid: "departments_search",
        labelField: "department",
        valueField: "department",
        el: $("#departments_dropdown")
    }).render();

    var SetupModel = KVStore.Model.extend({
        collectionName: 'ri_setup_coll'
    });

    var ViolationTypeModel = KVStore.Model.extend({
        collectionName: 'violation_types'
    });

    var ViolationTypeCollection = KVStore.Collection.extend({
        collectionName: 'violation_types',
        model: ViolationTypeModel
    });

    var oldSetupModelId;

    // Fetch setup data and populate form
    new SetupModel().fetch().then(function(setupDatas, textStatus, jqXHR) {
        new ViolationTypeCollection().fetch().then(function(violationTypes, textStatus, jqXHR) {
            var setupData;
            if (setupDatas.length == 0) {
                setupData = {
                    departments: [],
                    violationTypes: [],
                    learningTipsEnabled: 'True'
                };

                // Create new model upon save
                oldSetupModelId = "_new";
            } else {
                setupData = setupDatas[0];

                // Update existing model upon save
                oldSetupModelId = setupData._key
            }

            // Populate departments in the UI
            departmentsDropdown.val(setupData.departments || []);

            // Populate violation types in the UI
            if (violationTypes.length != DEFAULT_VIOLATION_TYPES.length) {
                violationTypes = DEFAULT_VIOLATION_TYPES
            }
            _.each(violationTypes, function(violationType) {
                var rowElement = $(VIOLATION_TYPE_ROW_TEMPLATE())
                    .appendTo($("#violation_types"));

                $('.name input', rowElement).val(violationType.title);
                $('.color input', rowElement).val(violationType.color);
                $('.weight input', rowElement).val(violationType.weight);
            });

            var learningTipsEnabled = (setupData.learningTipsEnabled === 'True');
            $('#learn_more_tips_toggle input').prop('checked', learningTipsEnabled)

            // Now that form is loaded, allow it to be saved
            $('#save').removeClass('disabled');
        });
    });

    // Using Splunk JS SDK, perform a runtime check of the eventgen utility installation and
    // provide a corresponding to the user.
    // Implementation alternative: add a button/checkbox to the Setup UI to enable/disable
    // the eventgen app or the eventgen modular input. This approach is more involved and requires issuing a REST API call.
    var service = mvc.createService();
    service.apps()
        .fetch(function(err, apps) {
            if (err) {
                sendDevLog( err);
                console.error(err);
                return;
            }

            // Show the Google Drive app configuration section if the app is present and enabled
            var googleDriveApp = apps.item('googledrive_addon');
            if (googleDriveApp && !googleDriveApp.state().content.disabled) {
                isOauthConfigured(); // checking oauth2 credential state on filesystem
                sendDevLog("Enabling Google Drive Add-on in Setup interface.");
                $('#googleDriveModule').removeClass('hide');
            }

            var eventgenApp = apps.item('eventgen')
            if (eventgenApp) {
                eventgenApp.fetch(function(err, eventgenApp) {
                    if (err) {
                        sendDevLog(err);
                        console.error(err);
                        return;
                    }

                    $('#eventgen-loading').addClass('hide');

                    if (eventgenApp.state().content.disabled) {
                        $('#eventgen-disabled').removeClass('hide');
                    } else if (!eventgenApp.state().content.disabled) {
                        $('#eventgen-success').removeClass('hide');
                    }
                });
            } else {
                $('#eventgen-loading').addClass('hide');
                $('#eventgen-notinstalled').removeClass('hide');
            }
        });

    // Google Drive OAuth2 Checks
    $("#getAuth").click(function() {
        var clientId = $("#clientId").val();
        var clientSecret = $("#clientSecret").val();

        if(clientId.length == 0) {
            sendUxLog("User didn't enter a Client ID");
            $("#clentIdError").removeClass('hide');
        } else {
            // hiding error prompt since input value is present
            $("#clentIdError").addClass('hide');
        }

        if(clientSecret.length == 0) {
            sendUxLog("User didn't enter a Client Secret");
            $("#clentSecretError").removeClass('hide');
        } else {
            // hiding error prompt since input value is present
            $("#clentSecretError").addClass('hide');
        }

        if(clientId.length > 0 && clientSecret.length > 0) {
            sendUxLog("Opening Google Authentication window for user to obtain Auth Code.");
            window.open(GOOGLE_SIGN_IN_BASE_URL + clientId, "popupWindow", "width=600,height=600,scrollbars=yes");
            $("#codeEntry").removeClass('hide');
            $("#clentIdError").addClass('hide');
            $("#clentSecretError").addClass('hide');
        }
    });

    $("#saveAuth").click(function() {
        var client_id = $("#clientId").val()
        var client_secret = $("#clientSecret").val()
        var auth_code = $("#authCode").val()
        if(auth_code.length > 0) {
            // Creating OAuth2 object for key exchange
            var oauth2_record = {
                "auth_code": auth_code,
                "client_id" : client_id,
                "client_secret" : client_secret
            }

            // Attempting to exchange auth token for refresh token via call to custom RESTful endpoint
            // Details are located in restmap.conf
            var service = mvc.createService();
            service.post("/services/configure_oauth", oauth2_record,
                function(err, response) {
                    if(null!=response) {
                        $("#codeEntry").addClass('hide');
                        $("#gAuthSuccess").removeClass('hide');
                        $("#gAuthError").addClass('hide');
                    } else {
                        sendDevLog("Token exchange error: " + err.status + ". Message: " + err.error);
                        $("#gAuthError").removeClass('hide');
                        $("#gAuthSuccess").addClass('hide');
                    }
                });
            $("#authEntryError").addClass('hide');
        } else {
            $("#authEntryError").removeClass('hide');
            $("#gAuthSuccess").addClass('hide');
        }
    });

    // When save button clicked, update setup configuration
    $("#save").click(function() {
        if ($(this).hasClass('disabled')) {
            return;
        }

        // Clear previous validation error markers
        $('.violation_type .weight input').parent('span').removeClass("error");

        // Validate form fields and mark those in error
        var someEmpty = $('.violation_type .weight input').filter(function() {
            return !$.trim(this.value);
        }).parent('span').addClass("error").length > 0;

        var notNumbers = $('.violation_type .weight input').filter(function() {
            return !$.isNumeric(this.value);
        }).parent('span').addClass("error").length > 0;

        if (someEmpty) {
            sendUxLog("User left required fields blank.");
            window.alert("Some required fields have been left blank.");
        } else if (notNumbers) {
            sendUxLog("User entered invalid violation type number.");
            window.alert("Some specified violation type weights are not numbers.");
        } else {
            var violationTypes = [];
            _.each($('.violation_type'), function(violationTypeEl, index) {
                var viol_id = DEFAULT_VIOLATION_TYPES[index].id;
                var viol_title = DEFAULT_VIOLATION_TYPES[index].title;
                var viol_color = $('.color input', violationTypeEl).val();
                var viol_weight = $('.weight input', violationTypeEl).val();

                sendUxLog("Saving " + viol_title + ": user selected weight: " + viol_weight);
                violationTypes.push({
                    id: viol_id,
                    title: viol_title,
                    color: viol_color,
                    weight: viol_weight
                });
            });

            var tipsEnabled = $('#learn_more_tips_toggle input').prop('checked') ? 'True' : 'False';
            sendUxLog( "Learn more tips checked: " + tipsEnabled)
            var departmentSelection = departmentsDropdown.val();

            var selectedDepartments = "User selected departments: ";
            for (var i=0; i<departmentSelection.length; i++) {
                selectedDepartments += departmentSelection[i] + " ";
            }
            sendUxLog(selectedDepartments);

            var newSetupData = {
                departments: departmentSelection,
                learningTipsEnabled: tipsEnabled
            };

            var newSetupModel = (oldSetupModelId == "_new")
                ? new SetupModel()
                : new SetupModel({ _key: oldSetupModelId });

            $('#error-message').hide();
            newSetupModel.save(newSetupData).then(function() {
                setCollectionData(
                    ViolationTypeCollection,
                    ViolationTypeModel,
                    violationTypes,
                    function() {
                        sendDevLog("Model saved with id " + newSetupModel.id);
                        console.log('Model saved with id ' + newSetupModel.id);
                        window.location.href = "./summary";
                    });
            }, function() {
                $('#error-message').show();
                sendUxLog("Unable to save changes!");
            });
        }
    });

    function sendUxLog(message) {
        if(dev_debug_key.length>0) {
            sendLog(message, ux_logging_key);
        } else {
            console.log("UX Logging - No HTTP Input Key has been set!  Event logging disabled.");
        }
    }

    function sendDevLog(message) {
        if(dev_debug_key.length>0) {
            sendLog(message, dev_debug_key);
        } else {
            console.log("Dev Debug - No HTTP Input Key has been set!  Event logging disabled.");
        }
    }

    // Create the XHR object.
    function createCORSRequest(method, url) {
        var xhr = new XMLHttpRequest();
        if ("withCredentials" in xhr) {
            // XHR for Chrome/Firefox/Opera/Safari.
            xhr.open(method, url, true);
        } else if (typeof XDomainRequest != "undefined") {
            // XDomainRequest for IE.
            xhr = new XDomainRequest();
            xhr.open(method, url);
        } else {
            // CORS not supported.
            xhr = null;
        }
        return xhr;
    }

    function sendLog(message, authCode) {
        var http_request = new XMLHttpRequest();
        var http_input_url = "http://lappy386:8088/services/collector";
        // Using lower-level call to XMLHttpRequest due to issues with how
        // JQuery handles CORS requests
        var xhr = createCORSRequest('POST', http_input_url);
        xhr.setRequestHeader('Content-Type', 'text/plain');
        xhr.setRequestHeader('Authorization', 'Splunk ' + authCode);

        var log_message = {
            "event":
                {
                    "username": currentUser,
                    "message": message
                }
            };

            xhr.send(JSON.stringify(log_message));
  }

    // Replaces the contents of the specified collection with
    // new models initialized with the specified data.
    function setCollectionData(Collection, Model, modelDatas, done) {
        destroyModelsIn(Collection, function() {
            saveModels(Model, modelDatas, done);
        });
    }

    // Destroys all models in the specified KV Store collection.
    function destroyModelsIn(Collection, done) {
        var collection = new Collection();
        collection.fetch().then(function() {
            var modelIndex = collection.models.length - 1;

            var deleteLoop = function() {
                if (modelIndex >= 0) {
                    collection.models[modelIndex].destroy().then(function() {
                        modelIndex--;
                        deleteLoop();
                    });
                } else {
                    done();
                }
            };
            deleteLoop();
        });
    }

    // Saves all specified models.
    function saveModels(Model, modelDatas, done) {
        var modelIndex = 0;

        var saveLoop = function() {
            if (modelIndex < modelDatas.length) {
                new Model().save(modelDatas[modelIndex]).then(function() {
                    modelIndex++;
                    saveLoop();
                });
            } else {
                done();
            }
        };
        saveLoop();
    }

    // Determines whether or not the Google Drive OAuth2
    // credentials have been generated and shows UI element
    // indicating credential status
    function isOauthConfigured() {
        var service = mvc.createService();
        service.get('/services/configure_oauth/status?check=configured', "",
            function(err, response) {
                if(JSON.parse(response.data).configured==true) {
                    $('#gAuthNotConfigured').addClass('hide');
                    $('#gAuthConfigured').removeClass('hide');
                } else {
                    $('#gAuthConfigured').addClass('hide');
                    $('#gAuthNotConfigured').removeClass('hide');
                }
            });
    }
});<commit_msg>Corrected reference to UX HTTP Input key.  Thanks, Matt!<commit_after>require([
    'splunkjs/ready!',
    'splunkjs/mvc/simplexml/ready!',
    'underscore',
    '../app/pas_ref_app/components/kvstore_backbone/kvstore',
    'splunkjs/mvc/multidropdownview'
], function(mvc, ignored, _, KVStore, MultiDropdownView) {
    // TODO: Add error handling for I/O errors.
    //       No time to fix now since feature freeze in a few hours...

    var GOOGLE_SIGN_IN_BASE_URL = "https://accounts.google.com/o/oauth2/auth?redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fadmin.reports.audit.readonly&client_id=";

    var logService = mvc.createService();
    var currentUser = Splunk.util.getConfigValue("USERNAME");
    var dev_debug_key = "";
    var ux_logging_key = "";

    var VIOLATION_TYPE_ROW_TEMPLATE =
        _.template(
            "
"+
                "Name: <input type=\"text\" disabled/> " +
                "Color: <input type=\"text\"/ disabled> " +
                "Weight: <input type=\"text\"/>" +
            "
");

    var DEFAULT_VIOLATION_TYPES = [
        {
            id: 'Invalid_Time_Access',
            title: 'Off-Hours Access',
            color: 'Yellow',
            weight: 1.2
        },
        {
            id: 'Terminated_Access',
            title: 'Terminated Employee Access',
            color: 'Red',
            weight: 3.0
        },
        {
            id: 'Excessive_Access',
            title: 'Users with Excessive Accesses',
            color: 'Yellow',
            weight: 1.0
        }
    ];

    var departmentsDropdown = new MultiDropdownView({
        managerid: "departments_search",
        labelField: "department",
        valueField: "department",
        el: $("#departments_dropdown")
    }).render();

    var SetupModel = KVStore.Model.extend({
        collectionName: 'ri_setup_coll'
    });

    var ViolationTypeModel = KVStore.Model.extend({
        collectionName: 'violation_types'
    });

    var ViolationTypeCollection = KVStore.Collection.extend({
        collectionName: 'violation_types',
        model: ViolationTypeModel
    });

    var oldSetupModelId;

    // Fetch setup data and populate form
    new SetupModel().fetch().then(function(setupDatas, textStatus, jqXHR) {
        new ViolationTypeCollection().fetch().then(function(violationTypes, textStatus, jqXHR) {
            var setupData;
            if (setupDatas.length == 0) {
                setupData = {
                    departments: [],
                    violationTypes: [],
                    learningTipsEnabled: 'True'
                };

                // Create new model upon save
                oldSetupModelId = "_new";
            } else {
                setupData = setupDatas[0];

                // Update existing model upon save
                oldSetupModelId = setupData._key
            }

            // Populate departments in the UI
            departmentsDropdown.val(setupData.departments || []);

            // Populate violation types in the UI
            if (violationTypes.length != DEFAULT_VIOLATION_TYPES.length) {
                violationTypes = DEFAULT_VIOLATION_TYPES
            }
            _.each(violationTypes, function(violationType) {
                var rowElement = $(VIOLATION_TYPE_ROW_TEMPLATE())
                    .appendTo($("#violation_types"));

                $('.name input', rowElement).val(violationType.title);
                $('.color input', rowElement).val(violationType.color);
                $('.weight input', rowElement).val(violationType.weight);
            });

            var learningTipsEnabled = (setupData.learningTipsEnabled === 'True');
            $('#learn_more_tips_toggle input').prop('checked', learningTipsEnabled)

            // Now that form is loaded, allow it to be saved
            $('#save').removeClass('disabled');
        });
    });

    // Using Splunk JS SDK, perform a runtime check of the eventgen utility installation and
    // provide a corresponding to the user.
    // Implementation alternative: add a button/checkbox to the Setup UI to enable/disable
    // the eventgen app or the eventgen modular input. This approach is more involved and requires issuing a REST API call.
    var service = mvc.createService();
    service.apps()
        .fetch(function(err, apps) {
            if (err) {
                sendDevLog( err);
                console.error(err);
                return;
            }

            // Show the Google Drive app configuration section if the app is present and enabled
            var googleDriveApp = apps.item('googledrive_addon');
            if (googleDriveApp && !googleDriveApp.state().content.disabled) {
                isOauthConfigured(); // checking oauth2 credential state on filesystem
                sendDevLog("Enabling Google Drive Add-on in Setup interface.");
                $('#googleDriveModule').removeClass('hide');
            }

            var eventgenApp = apps.item('eventgen')
            if (eventgenApp) {
                eventgenApp.fetch(function(err, eventgenApp) {
                    if (err) {
                        sendDevLog(err);
                        console.error(err);
                        return;
                    }

                    $('#eventgen-loading').addClass('hide');

                    if (eventgenApp.state().content.disabled) {
                        $('#eventgen-disabled').removeClass('hide');
                    } else if (!eventgenApp.state().content.disabled) {
                        $('#eventgen-success').removeClass('hide');
                    }
                });
            } else {
                $('#eventgen-loading').addClass('hide');
                $('#eventgen-notinstalled').removeClass('hide');
            }
        });

    // Google Drive OAuth2 Checks
    $("#getAuth").click(function() {
        var clientId = $("#clientId").val();
        var clientSecret = $("#clientSecret").val();

        if(clientId.length == 0) {
            sendUxLog("User didn't enter a Client ID");
            $("#clentIdError").removeClass('hide');
        } else {
            // hiding error prompt since input value is present
            $("#clentIdError").addClass('hide');
        }

        if(clientSecret.length == 0) {
            sendUxLog("User didn't enter a Client Secret");
            $("#clentSecretError").removeClass('hide');
        } else {
            // hiding error prompt since input value is present
            $("#clentSecretError").addClass('hide');
        }

        if(clientId.length > 0 && clientSecret.length > 0) {
            sendUxLog("Opening Google Authentication window for user to obtain Auth Code.");
            window.open(GOOGLE_SIGN_IN_BASE_URL + clientId, "popupWindow", "width=600,height=600,scrollbars=yes");
            $("#codeEntry").removeClass('hide');
            $("#clentIdError").addClass('hide');
            $("#clentSecretError").addClass('hide');
        }
    });

    $("#saveAuth").click(function() {
        var client_id = $("#clientId").val()
        var client_secret = $("#clientSecret").val()
        var auth_code = $("#authCode").val()
        if(auth_code.length > 0) {
            // Creating OAuth2 object for key exchange
            var oauth2_record = {
                "auth_code": auth_code,
                "client_id" : client_id,
                "client_secret" : client_secret
            }

            // Attempting to exchange auth token for refresh token via call to custom RESTful endpoint
            // Details are located in restmap.conf
            var service = mvc.createService();
            service.post("/services/configure_oauth", oauth2_record,
                function(err, response) {
                    if(null!=response) {
                        $("#codeEntry").addClass('hide');
                        $("#gAuthSuccess").removeClass('hide');
                        $("#gAuthError").addClass('hide');
                    } else {
                        sendDevLog("Token exchange error: " + err.status + ". Message: " + err.error);
                        $("#gAuthError").removeClass('hide');
                        $("#gAuthSuccess").addClass('hide');
                    }
                });
            $("#authEntryError").addClass('hide');
        } else {
            $("#authEntryError").removeClass('hide');
            $("#gAuthSuccess").addClass('hide');
        }
    });

    // When save button clicked, update setup configuration
    $("#save").click(function() {
        if ($(this).hasClass('disabled')) {
            return;
        }

        // Clear previous validation error markers
        $('.violation_type .weight input').parent('span').removeClass("error");

        // Validate form fields and mark those in error
        var someEmpty = $('.violation_type .weight input').filter(function() {
            return !$.trim(this.value);
        }).parent('span').addClass("error").length > 0;

        var notNumbers = $('.violation_type .weight input').filter(function() {
            return !$.isNumeric(this.value);
        }).parent('span').addClass("error").length > 0;

        if (someEmpty) {
            sendUxLog("User left required fields blank.");
            window.alert("Some required fields have been left blank.");
        } else if (notNumbers) {
            sendUxLog("User entered invalid violation type number.");
            window.alert("Some specified violation type weights are not numbers.");
        } else {
            var violationTypes = [];
            _.each($('.violation_type'), function(violationTypeEl, index) {
                var viol_id = DEFAULT_VIOLATION_TYPES[index].id;
                var viol_title = DEFAULT_VIOLATION_TYPES[index].title;
                var viol_color = $('.color input', violationTypeEl).val();
                var viol_weight = $('.weight input', violationTypeEl).val();

                sendUxLog("Saving " + viol_title + ": user selected weight: " + viol_weight);
                violationTypes.push({
                    id: viol_id,
                    title: viol_title,
                    color: viol_color,
                    weight: viol_weight
                });
            });

            var tipsEnabled = $('#learn_more_tips_toggle input').prop('checked') ? 'True' : 'False';
            sendUxLog( "Learn more tips checked: " + tipsEnabled)
            var departmentSelection = departmentsDropdown.val();

            var selectedDepartments = "User selected departments: ";
            for (var i=0; i<departmentSelection.length; i++) {
                selectedDepartments += departmentSelection[i] + " ";
            }
            sendUxLog(selectedDepartments);

            var newSetupData = {
                departments: departmentSelection,
                learningTipsEnabled: tipsEnabled
            };

            var newSetupModel = (oldSetupModelId == "_new")
                ? new SetupModel()
                : new SetupModel({ _key: oldSetupModelId });

            $('#error-message').hide();
            newSetupModel.save(newSetupData).then(function() {
                setCollectionData(
                    ViolationTypeCollection,
                    ViolationTypeModel,
                    violationTypes,
                    function() {
                        sendDevLog("Model saved with id " + newSetupModel.id);
                        console.log('Model saved with id ' + newSetupModel.id);
                        window.location.href = "./summary";
                    });
            }, function() {
                $('#error-message').show();
                sendUxLog("Unable to save changes!");
            });
        }
    });

    function sendUxLog(message) {
        if(ux_logging_key.length>0) {
            sendLog(message, ux_logging_key);
        } else {
            console.log("UX Logging - No HTTP Input Key has been set!  Event logging disabled.");
        }
    }

    function sendDevLog(message) {
        if(dev_debug_key.length>0) {
            sendLog(message, dev_debug_key);
        } else {
            console.log("Dev Debug - No HTTP Input Key has been set!  Event logging disabled.");
        }
    }

    // Create the XHR object.
    function createCORSRequest(method, url) {
        var xhr = new XMLHttpRequest();
        if ("withCredentials" in xhr) {
            // XHR for Chrome/Firefox/Opera/Safari.
            xhr.open(method, url, true);
        } else if (typeof XDomainRequest != "undefined") {
            // XDomainRequest for IE.
            xhr = new XDomainRequest();
            xhr.open(method, url);
        } else {
            // CORS not supported.
            xhr = null;
        }
        return xhr;
    }

    function sendLog(message, authCode) {
        var http_request = new XMLHttpRequest();
        var http_input_url = "http://lappy386:8088/services/collector";
        // Using lower-level call to XMLHttpRequest due to issues with how
        // JQuery handles CORS requests
        var xhr = createCORSRequest('POST', http_input_url);
        xhr.setRequestHeader('Content-Type', 'text/plain');
        xhr.setRequestHeader('Authorization', 'Splunk ' + authCode);

        var log_message = {
            "event":
                {
                    "username": currentUser,
                    "message": message
                }
            };

            xhr.send(JSON.stringify(log_message));
  }

    // Replaces the contents of the specified collection with
    // new models initialized with the specified data.
    function setCollectionData(Collection, Model, modelDatas, done) {
        destroyModelsIn(Collection, function() {
            saveModels(Model, modelDatas, done);
        });
    }

    // Destroys all models in the specified KV Store collection.
    function destroyModelsIn(Collection, done) {
        var collection = new Collection();
        collection.fetch().then(function() {
            var modelIndex = collection.models.length - 1;

            var deleteLoop = function() {
                if (modelIndex >= 0) {
                    collection.models[modelIndex].destroy().then(function() {
                        modelIndex--;
                        deleteLoop();
                    });
                } else {
                    done();
                }
            };
            deleteLoop();
        });
    }

    // Saves all specified models.
    function saveModels(Model, modelDatas, done) {
        var modelIndex = 0;

        var saveLoop = function() {
            if (modelIndex < modelDatas.length) {
                new Model().save(modelDatas[modelIndex]).then(function() {
                    modelIndex++;
                    saveLoop();
                });
            } else {
                done();
            }
        };
        saveLoop();
    }

    // Determines whether or not the Google Drive OAuth2
    // credentials have been generated and shows UI element
    // indicating credential status
    function isOauthConfigured() {
        var service = mvc.createService();
        service.get('/services/configure_oauth/status?check=configured', "",
            function(err, response) {
                if(JSON.parse(response.data).configured==true) {
                    $('#gAuthNotConfigured').addClass('hide');
                    $('#gAuthConfigured').removeClass('hide');
                } else {
                    $('#gAuthConfigured').addClass('hide');
                    $('#gAuthNotConfigured').removeClass('hide');
                }
            });
    }
});<|endoftext|>


<commit_before>// This is a bit of a wreck right now. Don't look.
window.Dynamic = (function() {

	var _models = [];
	var _dynamicNodes = [];
	var _hideClass = 'dynamic-hide';

	/**
	 * Gets all elements with specified attribute
	 * Doesn't support 'for' attribute in ie7
	 * @private
	 * @param {String} node
	 * @param {String} attr
	 * @return {NodeList|Array} Matching nodes
	 */
	var _getElementsWithAttribute = function(node, attr) {
		if (node.querySelectorAll) {
			return node.querySelectorAll('[' + attr + ']');
		} else {
			var matchingNodes = [];
			var allNodes = node.getElementsByTagName('*');

			for (var i=0, iLen=allNodes.length; i<iLen; i++) {
				var thisNode = allNodes[i];
				if (thisNode.getAttribute(attr)) {
					matchingNodes.push(thisNode);
				}
			}
			return matchingNodes;
		}
	};

	/**
	 * Add an event listener
	 *
	 * @private
	 * @param {HTMLElement} node The node to add the listener to
	 * @param {String} evt Name of the event to listen for
	 * @param {Function} callback
	 */
	var _addEvent = function(node, evt, callback) {
		if (node.addEventListener) {
			node.addEventListener(evt, callback, false);
		} else if (node.attachEvent)  {
			node.attachEvent('on' + evt, callback);
		}
	};

	/**
	 * Delegated event listener for tags
	 *
	 * @private
	 * @Param {String} tag The type of element to listen for
	 * @param {String} evt Name of the event to listen for
	 * @param {Function} callback
	 * @param {HTMLElement} container Node to add the listener to
	 */
	var _addDelegateByTag = function(tag, evt, callback, container) {
		container = container || document.body;

		// Change events don't bubble in ie8-
		// Add listener to every tag instead (boo)
		if (evt === 'change' && !container.addEventListener) {
			var changeNodes = container.getElementsByTagName(tag);
			for (var i=0; i<changeNodes.length; i++) {
				_addEvent(changeNodes[i], evt, function(ev) {
					var target = ev.target || ev.srcElement;
					callback.apply(target);
				});
			}
			return;
		}

		_addEvent(container, evt, function(ev) {
			var target = ev.target || ev.srcElement;
			if (target.nodeName === tag.toUpperCase()) {
				callback.apply(target);
			}
		});
	};

	var _isVisible = function(node) {
		return node.offsetWidth > 0 && node.offsetHeight > 0;
	};

	var _getNodeValue = function(node) {
		if (node.type === 'checkbox' || node.type === 'radio') {
			return node.checked;
		}
		return node.value;
	};

	var _getRadioModelValue = function(radioModel) {
		for (var i=0, iLen=radioModel.node.length; i<iLen; i++) {
			var radio = radioModel.node[i];
			if (radio.checked) {
				return radio.value;
			}
		}
		return '';
	};

	var _initModel = function(node) {
		if (node.nodeName === 'INPUT' || node.nodeName === 'SELECT') {
			var modelName = node.getAttribute('data-model');

			if (node.type === 'radio') {
				if (_models[modelName]) {
					_models[modelName].node.push(node);
				} else {
					_models[modelName] = {
						node: [node],
						value: ''
					};
				}
				if (!node.getAttribute('name')) {
					node.setAttribute('name', modelName);
				}
				_models[modelName].value = _getRadioModelValue(_models[modelName]);
			} else {
				_models[modelName] = {
					node: node,
					value: _getNodeValue(node)
				};
			}

		} else {
			// garbage
		}
	};

	var _checkModel = function() {
		var modelName = this.getAttribute('data-model');

		if (modelName) {
			if (_models[modelName]) {
				var model = _models[modelName];
				var oldValue = model.value;

				if (this.type === 'radio') {
					model.value = _getRadioModelValue(model);
				} else {
					model.value = _getNodeValue(this);
				}

				if (model.value !== oldValue) {
					_applyRules();
				}
			} else {
				// create model?
			}
		}
	};

	var _applyRules = function() {
		var flattenedModels = _getFlattenedModels();
		var inputs = [];

		for (var i=0, iLen=_dynamicNodes.length; i<iLen; i++) {
			var node = _dynamicNodes[i][0];
			var childInputs = _dynamicNodes[i][1];
			var parsedExpressionValue;
			var expr = node.getAttribute('data-show');

			with (flattenedModels) {
				parsedExpressionValue = eval(expr); // it's either this or a large expression parsing library
			}

			//node.style.display = (parsedExpressionValue) ? '' : 'none';
			if (parsedExpressionValue) {
				node.classList.remove(_hideClass);
			} else {
				node.classList.add(_hideClass);
			}

			for (var k=0, kLen=childInputs.length; k<kLen; k++) {
				inputs.push(childInputs[k]);
			}
		}

		for (var i=0, iLen=inputs.length; i<iLen; i++) {
			var input = inputs[i];
			if (_isVisible(input)) {
				input.removeAttribute('disabled');
			} else {
				input.setAttribute('disabled', 'disabled');
			}
		}
	};

	var _getFlattenedModels = function() {
		var flattened = [];
		for (var i in _models) {
			flattened[i] = _models[i].value;
		}
		return flattened;
	};

	var _getSubmittableElements = function(node) {
		var nodes = [];
		var protoSlice = Array.prototype.slice;

		if (node.nodeName === 'INPUT' || node.nodeName === 'SELECT' || node.nodeName === 'TEXTAREA') {
			nodes.push(node);
		} else {
			var inputs = protoSlice.call(node.getElementsByTagName('input'));
			var selects = protoSlice.call(node.getElementsByTagName('select'));
			var textareas = protoSlice.call(node.getElementsByTagName('textarea'));

			nodes = inputs.concat(selects).concat(textareas);
		}

		return nodes;
	};

	var _init = function() {
		var modelList = _getElementsWithAttribute(document, 'data-model');
		for (var i=0, iLen=modelList.length; i<iLen; i++) {
			_initModel(modelList[i]);
		}

		var dynNodes = _getElementsWithAttribute(document, 'data-show');
		for (var i=0, iLen=dynNodes.length; i<iLen; i++) {
			var node = dynNodes[i];
			_dynamicNodes.push([node, _getSubmittableElements(node)]);
		}

		_applyRules();
	};

	(function initialize() {
		_init();

		_addDelegateByTag('input', 'click', _checkModel);
		_addDelegateByTag('input', 'change', _checkModel);
		_addDelegateByTag('input', 'keyup', _checkModel);
		_addDelegateByTag('select', 'change', _checkModel);

		document.write('<style type="text/css">.'+_hideClass+'{display:none!important;}</style>');
	})();

	return {
		_dynamicNodes: _dynamicNodes,
		_models: _models,
		init: _init
	};

})();
<commit_msg>consolidated submittable elements<commit_after>// This is a bit of a wreck right now. Don't look.
window.Dynamic = (function() {

	var _models = [];
	var _dynamicNodes = [];
	var _submittableElements = [];
	var _hideClass = 'dynamic-hide';

	/**
	 * Gets all elements with specified attribute
	 * Doesn't support 'for' attribute in ie7
	 * @private
	 * @param {String} node
	 * @param {String} attr
	 * @return {NodeList|Array} Matching nodes
	 */
	var _getElementsWithAttribute = function(node, attr) {
		if (node.querySelectorAll) {
			return node.querySelectorAll('[' + attr + ']');
		} else {
			var matchingNodes = [];
			var allNodes = node.getElementsByTagName('*');

			for (var i=0, iLen=allNodes.length; i<iLen; i++) {
				var thisNode = allNodes[i];
				if (thisNode.getAttribute(attr)) {
					matchingNodes.push(thisNode);
				}
			}
			return matchingNodes;
		}
	};

	/**
	 * Add an event listener
	 *
	 * @private
	 * @param {HTMLElement} node The node to add the listener to
	 * @param {String} evt Name of the event to listen for
	 * @param {Function} callback
	 */
	var _addEvent = function(node, evt, callback) {
		if (node.addEventListener) {
			node.addEventListener(evt, callback, false);
		} else if (node.attachEvent)  {
			node.attachEvent('on' + evt, callback);
		}
	};

	/**
	 * Delegated event listener for tags
	 *
	 * @private
	 * @Param {String} tag The type of element to listen for
	 * @param {String} evt Name of the event to listen for
	 * @param {Function} callback
	 * @param {HTMLElement} container Node to add the listener to
	 */
	var _addDelegateByTag = function(tag, evt, callback, container) {
		container = container || document.body;

		// Change events don't bubble in ie8-
		// Add listener to every tag instead (boo)
		if (evt === 'change' && !container.addEventListener) {
			var changeNodes = container.getElementsByTagName(tag);
			for (var i=0; i<changeNodes.length; i++) {
				_addEvent(changeNodes[i], evt, function(ev) {
					var target = ev.target || ev.srcElement;
					callback.apply(target);
				});
			}
			return;
		}

		_addEvent(container, evt, function(ev) {
			var target = ev.target || ev.srcElement;
			if (target.nodeName === tag.toUpperCase()) {
				callback.apply(target);
			}
		});
	};

	var _isVisible = function(node) {
		return node.offsetWidth > 0 && node.offsetHeight > 0;
	};

	var _getNodeValue = function(node) {
		if (node.type === 'checkbox' || node.type === 'radio') {
			return node.checked;
		}
		return node.value;
	};

	var _getRadioModelValue = function(radioModel) {
		for (var i=0, iLen=radioModel.node.length; i<iLen; i++) {
			var radio = radioModel.node[i];
			if (radio.checked) {
				return radio.value;
			}
		}
		return '';
	};

	var _initModel = function(node) {
		if (node.nodeName === 'INPUT' || node.nodeName === 'SELECT') {
			var modelName = node.getAttribute('data-model');

			if (node.type === 'radio') {
				if (_models[modelName]) {
					_models[modelName].node.push(node);
				} else {
					_models[modelName] = {
						node: [node],
						value: ''
					};
				}
				if (!node.getAttribute('name')) {
					node.setAttribute('name', modelName);
				}
				_models[modelName].value = _getRadioModelValue(_models[modelName]);
			} else {
				_models[modelName] = {
					node: node,
					value: _getNodeValue(node)
				};
			}
		} else {
			// garbage
		}
	};

	var _checkModel = function() {
		var modelName = this.getAttribute('data-model');

		if (modelName) {
			if (_models[modelName]) {
				var model = _models[modelName];
				var oldValue = model.value;

				if (this.type === 'radio') {
					model.value = _getRadioModelValue(model);
				} else {
					model.value = _getNodeValue(this);
				}

				if (model.value !== oldValue) {
					_applyRules();
				}
			} else {
				// create model?
			}
		}
	};

	var _applyRules = function() {
		var flattenedModels = _getFlattenedModels();
		var inputs = [];

		for (var i=0, iLen=_dynamicNodes.length; i<iLen; i++) {
			var node = _dynamicNodes[i];
			var parsedExpressionValue;
			var expr = node.getAttribute('data-show');

			with (flattenedModels) {
				parsedExpressionValue = eval(expr); // it's either this or a large expression parsing library
			}

			//node.style.display = (parsedExpressionValue) ? '' : 'none';
			if (parsedExpressionValue) {
				node.classList.remove(_hideClass);
			} else {
				node.classList.add(_hideClass);
			}

		}

		_checkSubmittableElements();
	};

	var _checkSubmittableElements = function() {
		for (var i=0, iLen=_submittableElements.length; i<iLen; i++) {
			var input = _submittableElements[i];
			if (_isVisible(input)) {
				input.removeAttribute('disabled');
			} else {
				input.setAttribute('disabled', 'disabled');
			}
		}
	}

	var _getFlattenedModels = function() {
		var flattened = [];
		for (var i in _models) {
			flattened[i] = _models[i].value;
		}
		return flattened;
	};

	var _getSubmittableElements = function(node) {
		var protoSlice = Array.prototype.slice;

		if (node.nodeName === 'INPUT' || node.nodeName === 'SELECT' || node.nodeName === 'TEXTAREA') {
			_submittableElements.push(node);
		} else {
			var inputs = protoSlice.call(node.getElementsByTagName('input'));
			var selects = protoSlice.call(node.getElementsByTagName('select'));
			var textareas = protoSlice.call(node.getElementsByTagName('textarea'));

			_submittableElements = _submittableElements.concat(inputs).concat(selects).concat(textareas);
		}
	};

	var _init = function() {
		var modelList = _getElementsWithAttribute(document, 'data-model');
		for (var i=0, iLen=modelList.length; i<iLen; i++) {
			_initModel(modelList[i]);
		}

		_dynamicNodes = [];
		var dynNodes = _getElementsWithAttribute(document, 'data-show');
		for (var i=0, iLen=dynNodes.length; i<iLen; i++) {
			var node = dynNodes[i];
			_dynamicNodes.push(node);
			_getSubmittableElements(node);
		}

		_applyRules();
	};

	(function initialize() {
		document.write('<style type="text/css">.'+_hideClass+'{display:none!important;}</style>');

		_init();

		_addDelegateByTag('input', 'click', _checkModel);
		_addDelegateByTag('input', 'change', _checkModel);
		_addDelegateByTag('input', 'keyup', _checkModel);
		_addDelegateByTag('select', 'change', _checkModel);
	})();

	return {
		_dynamicNodes: _dynamicNodes,
		_models: _models,
		reinit: _init
	};

})();
<|endoftext|>


<commit_before><commit_msg>JSON scene loader: pass the material "as is" if it is not a string<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated and other contributors.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

define(function (require, exports, module) {
	"use strict";

	var EditorManager = brackets.getModule("editor/EditorManager");
	var Inspector     = brackets.getModule("LiveDevelopment/Inspector/Inspector");
	var Main          = require("main");
	var Resizer       = brackets.getModule("utils/Resizer");

	var $panel, $toolbar;
	var _panel, _shown = false;

	function setPanel(panel) {
		if (panel === _panel) {
			return;
		}
		if (_panel) {
			_panel.remove();
		}
		_panel = panel;
		_panel.add($panel);
	}

	/** Toggle the display of the panel */
	function toggle(show) {
		if (show === undefined) {
			Resizer.toggle($panel.get(0));
		} else if (show) {
			Resizer.show($panel.get(0));
		} else {
			Resizer.hide($panel.get(0));
		}
		$panel.toggle(show);
	}

	function _enable() {
		// toggle(true);
	}

	function _disable() {
		toggle(false);
	}

	/** Initialize the panel */
	function init() {
		$panel = $("
").insertAfter(".bottom-panel:last");
		$toolbar = $("
").appendTo($panel);
		$("
").appendTo($toolbar).text("Log");
		$("").appendTo($toolbar).html("×");

		Resizer.makeResizable($panel.get(0),
		                      "vert" /* Resizer.DIRECTION_VERTICAL */,
		                      "top" /* Resizer.POSITION_TOP */,
		                      100, // minSize
		                      false // collapsible
		                      );

		$(Main).on("enable", _enable);
		$(Main).on("disable", _disable);
	}

	/** Unload the panel */
	function unload() {
		$(Main).off("enable", _enable);
		$(Main).off("disable", _disable);

		$panel.remove();
		$panel = undefined;
	}

	exports.init = init;
	exports.unload = unload;
	exports.toggle = toggle;
	exports.setPanel = setPanel;
});
<commit_msg>remove the panel's close button (it was non-functional anyway)<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated and other contributors.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

define(function (require, exports, module) {
	"use strict";

	var EditorManager = brackets.getModule("editor/EditorManager");
	var Inspector     = brackets.getModule("LiveDevelopment/Inspector/Inspector");
	var Main          = require("main");
	var Resizer       = brackets.getModule("utils/Resizer");

	var $panel, $toolbar;
	var _panel, _shown = false;

	function setPanel(panel) {
		if (panel === _panel) {
			return;
		}
		if (_panel) {
			_panel.remove();
		}
		_panel = panel;
		_panel.add($panel);
	}

	/** Toggle the display of the panel */
	function toggle(show) {
		if (show === undefined) {
			Resizer.toggle($panel.get(0));
		} else if (show) {
			Resizer.show($panel.get(0));
		} else {
			Resizer.hide($panel.get(0));
		}
		$panel.toggle(show);
	}

	function _enable() {
		// toggle(true);
	}

	function _disable() {
		toggle(false);
	}

	/** Initialize the panel */
	function init() {
		$panel = $("
").insertAfter(".bottom-panel:last");
		$toolbar = $("
").appendTo($panel);
		$("
").appendTo($toolbar).text("Log");

		Resizer.makeResizable($panel.get(0),
		                      "vert" /* Resizer.DIRECTION_VERTICAL */,
		                      "top" /* Resizer.POSITION_TOP */,
		                      100, // minSize
		                      false // collapsible
		                      );

		$(Main).on("enable", _enable);
		$(Main).on("disable", _disable);
	}

	/** Unload the panel */
	function unload() {
		$(Main).off("enable", _enable);
		$(Main).off("disable", _disable);

		$panel.remove();
		$panel = undefined;
	}

	exports.init = init;
	exports.unload = unload;
	exports.toggle = toggle;
	exports.setPanel = setPanel;
});
<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import { BinaryChart } from 'binary-charts'
import { logoutAllTokens } from 'binary-common-utils/lib/account'
import { observer } from 'binary-common-utils/lib/observer'
import { getTokenList, removeAllTokens, get as getStorage, set as setStorage, getToken,
} from 'binary-common-utils/lib/storageManager'
import TradeInfo from './tradeInfo'
import _Blockly from './blockly'
import { translator } from '../../common/translator'
import Welcome from './tours/welcome'
import Introduction from './tours/introduction'
import { logHandler } from './logger'
import { SaveXml } from './react-components/SaveXml'
import { RestartTimeout } from './react-components/RestartTimeout'
import { LimitsPanel } from './react-components/LimitsPanel'

let realityCheckTimeout

const showRealityCheck = () => {
  $('.blocker').show()
  $('.reality-check').show()
}

const hideRealityCheck = () => {
  $('#rc-err').hide()
  $('.blocker').hide()
  $('.reality-check').hide()
}

const stopRealityCheck = () => {
  clearInterval(realityCheckTimeout)
  realityCheckTimeout = null
}

const realityCheckInterval = () => {
  realityCheckTimeout = setInterval(() => {
    const now = parseInt((new Date().getTime()) / 1000, 10)
    const checkTime = +getStorage('realityCheckTime')
    if (checkTime && now >= checkTime) {
      showRealityCheck()
      stopRealityCheck()
    }
  }, 1000)
}

const startRealityCheck = (time, token) => {
  stopRealityCheck()
  if (time) {
    const start = parseInt((new Date().getTime()) / 1000, 10) + (time * 60)
    setStorage('realityCheckTime', start)
    realityCheckInterval()
  } else {
    const tokenObj = getToken(token)
    if (tokenObj.hasRealityCheck) {
      const checkTime = +getStorage('realityCheckTime')
      if (!checkTime) {
        showRealityCheck()
      } else {
        realityCheckInterval()
      }
    }
  }
}

const clearRealityCheck = () => {
  setStorage('realityCheckTime', null)
  stopRealityCheck()
}

const resetRealityCheck = (token) => {
  clearRealityCheck()
  startRealityCheck(null, token)
}

export default class View {
  constructor() {
    this.chartType = 'line'
    this.tours = {}
    logHandler()
    this.tradeInfo = new TradeInfo()
    this.addTranslationToUi()
    this.initPromise = new Promise((resolve) => {
      this.updateTokenList()
      this.blockly = new _Blockly()
      this.blockly.initPromise.then(() => {
        $('.actions_menu').show()
        this.setElementActions()
        this.initTours()
        $('#accountLis')
        startRealityCheck(null, $('.account-id').first().attr('value'))
        resolve()
      })
    })
  }
  updateTokenList() {
    const tokenList = getTokenList()
    const loginButton = $('#login')
    const accountList = $('#account-list')
    if (tokenList.length === 0) {
      loginButton.show()
      accountList.hide()
      $('.account-id').removeAttr('value').text('')
      $('.account-type').text('')
      $('.login-id-list').children().remove()
    } else {
      loginButton.hide()
      accountList.show()
      for (const tokenInfo of tokenList) {
        let prefix = ''
        if ('isVirtual' in tokenInfo) {
          prefix = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account'
        }
        if (tokenList.indexOf(tokenInfo) === 0) {
          $('.account-id').attr('value', `${tokenInfo.token}`)
            .text(`${tokenInfo.account_name}`)
          $('.account-type').text(`${prefix}`)
        } else {
          $('.login-id-list').append(`<a href="#" value="${tokenInfo.token}"><li><span>${prefix}</span><div>${tokenInfo.account_name}</div></li></a>` +
            '

')
        }
      }
    }
  }
  addTranslationToUi() {
    $('[data-i18n-text]')
      .each(function each() {
        const contents = $(this).contents()
        if (contents.length > 0) {
          if (contents.get(0).nodeType === Node.TEXT_NODE) {
            $(this).text(translator.translateText($(this)
              .attr('data-i18n-text')))
              .append(contents.slice(1))
          }
        } else {
          $(this)
            .text(translator.translateText($(this)
              .attr('data-i18n-text')))
        }
      })
  }
  initTours() {
    this.tours.introduction = new Introduction()
    this.tours.welcome = new Welcome()
  }
  startTour() {
    const viewScope = this
    $('#select-tour li:first')
      .nextAll().click((e) => {
         const value = $(e.target).attr('class')
         if (value === '') return
         if (viewScope.activeTour) {
            viewScope.activeTour.stop()
          }
          viewScope.activeTour = viewScope.tours[value]
          viewScope.activeTour.start(() => {
            viewScope.activeTour = null
          })
       })
  }
  setFileBrowser() {
    const readFile = (f, dropEvent = {}) => {
      const reader = new FileReader()
      reader.onload = e => this.blockly.load(e.target.result, dropEvent)
      reader.readAsText(f)
    }

    const handleFileSelect = (e) => {
      let files
      let dropEvent
      if (e.type === 'drop') {
        e.stopPropagation()
        e.preventDefault()
        files = e.dataTransfer.files
        dropEvent = e
      } else {
        files = e.target.files
      }
      files = [...files]
      for (const file of files) {
        if (file.type.match('text/xml')) {
          readFile(file, dropEvent)
        } else {
          observer.emit('ui.log.info', `${
          translator.translateText('File is not supported:')} ${file.name}`)
        }
      }
    }

    const handleDragOver = (e) => {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy' // eslint-disable-line no-param-reassign
    }

    const dropZone = document.body

    dropZone.addEventListener('dragover', handleDragOver, false)
    dropZone.addEventListener('drop', handleFileSelect, false)
    if (document.getElementById('files')) {
      document.getElementById('files')
        .addEventListener('change', handleFileSelect, false)
    }
    $('#open_btn')
      .on('click', () => {
        $.FileDialog({ // eslint-disable-line new-cap
          accept: '.xml',
          cancelButton: 'Close',
          dragMessage: 'Drop files here',
          dropheight: 400,
          errorMessage: 'An error occured while loading file',
          multiple: false,
          okButton: 'OK',
          readAs: 'DataURL',
          removeMessage: 'Remove file',
          title: 'Load file',
        })
      })
      .on('files.bs.filedialog', (ev) => {
        handleFileSelect(ev.files)
      })
      .on('cancel.bs.filedialog', (ev) => {
        handleFileSelect(ev)
      })
  }
  setElementActions() {
    this.setFileBrowser()
    this.startTour()
    this.addBindings()
    this.addEventHandlers()
  }
  addBindings() {
    const logout = () => {
      logoutAllTokens(() => {
        this.updateTokenList()
        observer.emit('ui.log.info', translator.translateText('Logged you out!'))
        clearRealityCheck()
      })
    }

    $('#stopButton')
      .click(e => {
        if (e) {
          e.preventDefault()
        }
        stopRealityCheck()
        window.Bot.stop()
      })
      .hide()

    $('.panelExitButton')
      .click(function onClick() {
        $(this)
          .parent()
          .hide()
      })

    $('.panel')
      .hide()

    $('.panel')
      .drags()

    $('.panel .content')
      .mousedown(e => e.stopPropagation()) // prevent content to trigger draggable

    $('.tours')
      .click(function onClick() {
        if ($('#select-tour').css('display') === 'none') {
          $('#select-tour')
            .fadeIn(100)
        }
        else {
          $('#select-tour')
            .fadeOut(100)
        }
      })

    $('#header, #blocklyDiv')
      .click(function onClick() {
        if ($('#select-tour').css('display') === 'block') {
          $('#select-tour')
            .fadeOut(100)
        }
      })

    ReactDOM.render(
      <SaveXml
      onSave={(filename, collection) => this.blockly.save(filename, collection)}
      />
      , $('#saveXml')[0])

    $('#saveXml')
      .click(() => {
        $('#saveAs').show()
      })

    $('#undo')
      .click(() => {
        this.blockly.undo()
      })

    $('#redo')
      .click(() => {
        this.blockly.redo()
      })

    $('#zoomIn')
      .click(() => {
        this.blockly.zoomOnPlusMinus(true)
      })

    $('#zoomOut')
      .click(() => {
        this.blockly.zoomOnPlusMinus(false)
      })

    $('#cleanUp')
      .click(() => {
        this.blockly.cleanUp()
      })

    $('#showSummary')
      .click(() => {
        $('#summaryPanel')
          .show()
      })

    $('#loadXml')
      .click(() => {
        $('#files')
          .click()
      })

    $('#logout, #logout-reality-check')
      .click(() => {
        logout()
        hideRealityCheck()
        $('.logout').hide()
      })

    $('#continue-trading')
      .click(() => {
        const time = parseInt($('#realityDuration').val(), 10)
        if (time >= 10 && time <= 120) {
          hideRealityCheck()
          startRealityCheck(time)
        } else {
          $('#rc-err').show()
        }
      })

    const startBot = (limitations) => {
      $('#stopButton').show()
      $('#runButton').hide()
      this.blockly.run(limitations)
    }

    $('#runButton')
      .click(() => {
        const token = $('.account-id').first().attr('value')
        const tokenObj = getToken(token)
        if (tokenObj && tokenObj.hasTradeLimitation) {
          ReactDOM.render(
            <LimitsPanel
            onSave={startBot}
            />
            , document.getElementById('limits-panel'))
        } else {
          startBot()
        }
      })

    $('#resetButton')
      .click(() => {
        this.blockly.resetWorkspace()
      })

    $('.login-id-list')
      .on('click', 'a', (e) => {
        resetRealityCheck($(e.currentTarget).attr('value'))
        e.preventDefault()
        const $el = $(e.currentTarget)
        const $oldType = $el.find('li span')
        const $oldTypeText = $oldType.text()
        const $oldID = $el.find('li div')
        const $oldIDText = $oldID.text()
        const $oldValue = $el.attr('value')
        const $newType = $('.account-type')
        const $newTypeText = $newType.first().text()
        const $newID = $('.account-id')
        const $newIDText = $newID.first().text()
        const $newValue = $newID.attr('value')
        $oldType.html($newTypeText)
        $oldID.html($newIDText)
        $el.attr('value', $newValue)
        $newType.html($oldTypeText)
        $newID.html($oldIDText)
        $newID.attr('value', $oldValue)
      })

    $('#login')
      .bind('click.login', () => {
        document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' +
          `${getStorage('appId')}&l=${translator.getLanguage().toUpperCase()}`
      })
      .text('Log in')

    $('#statement-reality-check').click(() => {
      document.location =
        `https://www.binary.com/${translator.getLanguage()}/user/statementws.html`
    })
    $(document).keydown((e) => {
      if (e.which === 189) { // -
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(false)
          e.preventDefault()
        }
      } else if (e.which === 187) { // +
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(true)
          e.preventDefault()
        }
      } else if (e.which === 39) { // right
        if (this.activeTour) {
          this.activeTour.next()
          e.preventDefault()
        }
      } else if (e.which === 27) { // Esc
        const exitButton = $('.panel:hover .panelExitButton')
        if (exitButton.length === 1) {
          exitButton.click()
          e.preventDefault()
        }
      }
    })
  }
  updateChart(info) {
    const chartToDataType = {
      area: 'ticks',
      line: 'ticks',
      candlestick: 'ohlc',
      ohlc: 'ohlc',
    }

    const isLine = () => ['area', 'line'].indexOf(this.chartType) >= 0

    const zoomInMax = (ev, chart) => {
      const {
        dataMax,
      } = chart.xAxis[0].getExtremes()
      const {
        minRange,
      } = chart.xAxis[0].options
      chart.xAxis[0].setExtremes(dataMax - minRange, dataMax)
    }

    const events = [
      {
        type: 'zoom-in-max',
        handler: zoomInMax,
      },
    ]

    if (isLine() && this.contractForChart) {
      const chartDiv = document.getElementById('trade-chart0')
      if (chartDiv) {
        chartDiv.dispatchEvent(new Event('zoom-in-max'))
      }
    }
    ReactDOM.render(
      <BinaryChart
      className="trade-chart"
      id="trade-chart0"
      contract={isLine() ? this.contractForChart : false}
      hideZoomControls={isLine() && this.contractForChart}
      pipSize={info.pipSize}
      shiftMode={this.contractForChart ? 'dynamic' : 'fixed'}
      ticks={info[chartToDataType[this.chartType]]}
      type={this.chartType}
      events={events}
      hideIntervalPicker
      onTypeChange={(type) => (this.chartType = type)}
      />, $('#chart')[0])
  }
  addEventHandlers() {
    for (const errorType of ['api.error', 'BlocklyError', 'RuntimeError']) {
      observer.register(errorType, (error) => { // eslint-disable-line no-loop-func
        if (error.error && error.error.code === 'InvalidToken') {
          removeAllTokens()
          this.updateTokenList()
        }
        window.Bot.stop()
        if (window.Bot.shouldRestartOnError()) {
          ReactDOM.render(
            <RestartTimeout
            timeout="3"
            />
            , document.getElementById('restartTimeout'))
        }
      })
    }

    observer.register('bot.stop', () => {
      $('#runButton').show()
      $('#stopButton').hide()
    })

    observer.register('bot.tradeInfo', (tradeInfo) => {
      for (const key of Object.keys(tradeInfo)) {
        this.tradeInfo.tradeInfo[key] = tradeInfo[key]
      }
      this.tradeInfo.update()
    })

    observer.register('bot.tradeUpdate', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = {
        ...contract,
      }
      this.contractForChart.date_expiry = Number(this.contractForChart.date_expiry)
      this.contractForChart.date_settlement = Number(this.contractForChart.date_settlement)
      this.contractForChart.date_start = Number(this.contractForChart.date_start)
    })

    observer.register('bot.finish', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = false
    })

    observer.register('bot.tickUpdate', (info) => {
      this.updateChart(info)
    })
  }
}
<commit_msg>stop event propagation on dropdown<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import { BinaryChart } from 'binary-charts'
import { logoutAllTokens } from 'binary-common-utils/lib/account'
import { observer } from 'binary-common-utils/lib/observer'
import { getTokenList, removeAllTokens, get as getStorage, set as setStorage, getToken,
} from 'binary-common-utils/lib/storageManager'
import TradeInfo from './tradeInfo'
import _Blockly from './blockly'
import { translator } from '../../common/translator'
import Welcome from './tours/welcome'
import Introduction from './tours/introduction'
import { logHandler } from './logger'
import { SaveXml } from './react-components/SaveXml'
import { RestartTimeout } from './react-components/RestartTimeout'
import { LimitsPanel } from './react-components/LimitsPanel'

let realityCheckTimeout

const showRealityCheck = () => {
  $('.blocker').show()
  $('.reality-check').show()
}

const hideRealityCheck = () => {
  $('#rc-err').hide()
  $('.blocker').hide()
  $('.reality-check').hide()
}

const stopRealityCheck = () => {
  clearInterval(realityCheckTimeout)
  realityCheckTimeout = null
}

const realityCheckInterval = () => {
  realityCheckTimeout = setInterval(() => {
    const now = parseInt((new Date().getTime()) / 1000, 10)
    const checkTime = +getStorage('realityCheckTime')
    if (checkTime && now >= checkTime) {
      showRealityCheck()
      stopRealityCheck()
    }
  }, 1000)
}

const startRealityCheck = (time, token) => {
  stopRealityCheck()
  if (time) {
    const start = parseInt((new Date().getTime()) / 1000, 10) + (time * 60)
    setStorage('realityCheckTime', start)
    realityCheckInterval()
  } else {
    const tokenObj = getToken(token)
    if (tokenObj.hasRealityCheck) {
      const checkTime = +getStorage('realityCheckTime')
      if (!checkTime) {
        showRealityCheck()
      } else {
        realityCheckInterval()
      }
    }
  }
}

const clearRealityCheck = () => {
  setStorage('realityCheckTime', null)
  stopRealityCheck()
}

const resetRealityCheck = (token) => {
  clearRealityCheck()
  startRealityCheck(null, token)
}

export default class View {
  constructor() {
    this.chartType = 'line'
    this.tours = {}
    logHandler()
    this.tradeInfo = new TradeInfo()
    this.addTranslationToUi()
    this.initPromise = new Promise((resolve) => {
      this.updateTokenList()
      this.blockly = new _Blockly()
      this.blockly.initPromise.then(() => {
        $('.actions_menu').show()
        this.setElementActions()
        this.initTours()
        $('#accountLis')
        startRealityCheck(null, $('.account-id').first().attr('value'))
        resolve()
      })
    })
  }
  updateTokenList() {
    const tokenList = getTokenList()
    const loginButton = $('#login')
    const accountList = $('#account-list')
    if (tokenList.length === 0) {
      loginButton.show()
      accountList.hide()
      $('.account-id').removeAttr('value').text('')
      $('.account-type').text('')
      $('.login-id-list').children().remove()
    } else {
      loginButton.hide()
      accountList.show()
      for (const tokenInfo of tokenList) {
        let prefix = ''
        if ('isVirtual' in tokenInfo) {
          prefix = (tokenInfo.isVirtual) ? 'Virtual Account' : 'Real Account'
        }
        if (tokenList.indexOf(tokenInfo) === 0) {
          $('.account-id').attr('value', `${tokenInfo.token}`)
            .text(`${tokenInfo.account_name}`)
          $('.account-type').text(`${prefix}`)
        } else {
          $('.login-id-list').append(`<a href="#" value="${tokenInfo.token}"><li><span>${prefix}</span><div>${tokenInfo.account_name}</div></li></a>` +
            '

')
        }
      }
    }
  }
  addTranslationToUi() {
    $('[data-i18n-text]')
      .each(function each() {
        const contents = $(this).contents()
        if (contents.length > 0) {
          if (contents.get(0).nodeType === Node.TEXT_NODE) {
            $(this).text(translator.translateText($(this)
              .attr('data-i18n-text')))
              .append(contents.slice(1))
          }
        } else {
          $(this)
            .text(translator.translateText($(this)
              .attr('data-i18n-text')))
        }
      })
  }
  initTours() {
    this.tours.introduction = new Introduction()
    this.tours.welcome = new Welcome()
  }
  startTour() {
    const viewScope = this
    $('#select-tour li:first')
      .nextAll().click((e) => {
         const value = $(e.target).attr('class')
         if (value === '') return
         if (viewScope.activeTour) {
            viewScope.activeTour.stop()
          }
          viewScope.activeTour = viewScope.tours[value]
          viewScope.activeTour.start(() => {
            viewScope.activeTour = null
          })
       })
  }
  setFileBrowser() {
    const readFile = (f, dropEvent = {}) => {
      const reader = new FileReader()
      reader.onload = e => this.blockly.load(e.target.result, dropEvent)
      reader.readAsText(f)
    }

    const handleFileSelect = (e) => {
      let files
      let dropEvent
      if (e.type === 'drop') {
        e.stopPropagation()
        e.preventDefault()
        files = e.dataTransfer.files
        dropEvent = e
      } else {
        files = e.target.files
      }
      files = [...files]
      for (const file of files) {
        if (file.type.match('text/xml')) {
          readFile(file, dropEvent)
        } else {
          observer.emit('ui.log.info', `${
          translator.translateText('File is not supported:')} ${file.name}`)
        }
      }
    }

    const handleDragOver = (e) => {
      e.stopPropagation()
      e.preventDefault()
      e.dataTransfer.dropEffect = 'copy' // eslint-disable-line no-param-reassign
    }

    const dropZone = document.body

    dropZone.addEventListener('dragover', handleDragOver, false)
    dropZone.addEventListener('drop', handleFileSelect, false)
    if (document.getElementById('files')) {
      document.getElementById('files')
        .addEventListener('change', handleFileSelect, false)
    }
    $('#open_btn')
      .on('click', () => {
        $.FileDialog({ // eslint-disable-line new-cap
          accept: '.xml',
          cancelButton: 'Close',
          dragMessage: 'Drop files here',
          dropheight: 400,
          errorMessage: 'An error occured while loading file',
          multiple: false,
          okButton: 'OK',
          readAs: 'DataURL',
          removeMessage: 'Remove file',
          title: 'Load file',
        })
      })
      .on('files.bs.filedialog', (ev) => {
        handleFileSelect(ev.files)
      })
      .on('cancel.bs.filedialog', (ev) => {
        handleFileSelect(ev)
      })
  }
  setElementActions() {
    this.setFileBrowser()
    this.startTour()
    this.addBindings()
    this.addEventHandlers()
  }
  addBindings() {
    const logout = () => {
      logoutAllTokens(() => {
        this.updateTokenList()
        observer.emit('ui.log.info', translator.translateText('Logged you out!'))
        clearRealityCheck()
      })
    }

    $('#stopButton')
      .click(e => {
        if (e) {
          e.preventDefault()
        }
        stopRealityCheck()
        window.Bot.stop()
      })
      .hide()

    $('.panelExitButton')
      .click(function onClick() {
        $(this)
          .parent()
          .hide()
      })

    $('.panel')
      .hide()

    $('.panel')
      .drags()

    $('.panel .content')
      .mousedown(e => e.stopPropagation()) // prevent content to trigger draggable

    $('.tours')
      .click(e => {
        e.stopPropagation()
        if ($('#select-tour').css('display') === 'none') {
          $('#select-tour')
            .fadeIn(100)
        } else {
          $('#select-tour')
            .fadeOut(100)
        }
      })

    $('body')
      .click(e => {
        if ($('#select-tour').css('display') === 'block') {
          $('#select-tour')
            .fadeOut(100)
        }
      })

    ReactDOM.render(
      <SaveXml
      onSave={(filename, collection) => this.blockly.save(filename, collection)}
      />
      , $('#saveXml')[0])

    $('#saveXml')
      .click(() => {
        $('#saveAs').show()
      })

    $('#undo')
      .click(() => {
        this.blockly.undo()
      })

    $('#redo')
      .click(() => {
        this.blockly.redo()
      })

    $('#zoomIn')
      .click(() => {
        this.blockly.zoomOnPlusMinus(true)
      })

    $('#zoomOut')
      .click(() => {
        this.blockly.zoomOnPlusMinus(false)
      })

    $('#cleanUp')
      .click(() => {
        this.blockly.cleanUp()
      })

    $('#showSummary')
      .click(() => {
        $('#summaryPanel')
          .show()
      })

    $('#loadXml')
      .click(() => {
        $('#files')
          .click()
      })

    $('#logout, #logout-reality-check')
      .click(() => {
        logout()
        hideRealityCheck()
        $('.logout').hide()
      })

    $('#continue-trading')
      .click(() => {
        const time = parseInt($('#realityDuration').val(), 10)
        if (time >= 10 && time <= 120) {
          hideRealityCheck()
          startRealityCheck(time)
        } else {
          $('#rc-err').show()
        }
      })

    const startBot = (limitations) => {
      $('#stopButton').show()
      $('#runButton').hide()
      this.blockly.run(limitations)
    }

    $('#runButton')
      .click(() => {
        const token = $('.account-id').first().attr('value')
        const tokenObj = getToken(token)
        if (tokenObj && tokenObj.hasTradeLimitation) {
          ReactDOM.render(
            <LimitsPanel
            onSave={startBot}
            />
            , document.getElementById('limits-panel'))
        } else {
          startBot()
        }
      })

    $('#resetButton')
      .click(() => {
        this.blockly.resetWorkspace()
      })

    $('.login-id-list')
      .on('click', 'a', (e) => {
        resetRealityCheck($(e.currentTarget).attr('value'))
        e.preventDefault()
        const $el = $(e.currentTarget)
        const $oldType = $el.find('li span')
        const $oldTypeText = $oldType.text()
        const $oldID = $el.find('li div')
        const $oldIDText = $oldID.text()
        const $oldValue = $el.attr('value')
        const $newType = $('.account-type')
        const $newTypeText = $newType.first().text()
        const $newID = $('.account-id')
        const $newIDText = $newID.first().text()
        const $newValue = $newID.attr('value')
        $oldType.html($newTypeText)
        $oldID.html($newIDText)
        $el.attr('value', $newValue)
        $newType.html($oldTypeText)
        $newID.html($oldIDText)
        $newID.attr('value', $oldValue)
      })

    $('#login')
      .bind('click.login', () => {
        document.location = 'https://oauth.binary.com/oauth2/authorize?app_id=' +
          `${getStorage('appId')}&l=${translator.getLanguage().toUpperCase()}`
      })
      .text('Log in')

    $('#statement-reality-check').click(() => {
      document.location =
        `https://www.binary.com/${translator.getLanguage()}/user/statementws.html`
    })
    $(document).keydown((e) => {
      if (e.which === 189) { // -
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(false)
          e.preventDefault()
        }
      } else if (e.which === 187) { // +
        if (e.ctrlKey) {
          this.blockly.zoomOnPlusMinus(true)
          e.preventDefault()
        }
      } else if (e.which === 39) { // right
        if (this.activeTour) {
          this.activeTour.next()
          e.preventDefault()
        }
      } else if (e.which === 27) { // Esc
        const exitButton = $('.panel:hover .panelExitButton')
        if (exitButton.length === 1) {
          exitButton.click()
          e.preventDefault()
        }
      }
    })
  }
  updateChart(info) {
    const chartToDataType = {
      area: 'ticks',
      line: 'ticks',
      candlestick: 'ohlc',
      ohlc: 'ohlc',
    }

    const isLine = () => ['area', 'line'].indexOf(this.chartType) >= 0

    const zoomInMax = (ev, chart) => {
      const {
        dataMax,
      } = chart.xAxis[0].getExtremes()
      const {
        minRange,
      } = chart.xAxis[0].options
      chart.xAxis[0].setExtremes(dataMax - minRange, dataMax)
    }

    const events = [
      {
        type: 'zoom-in-max',
        handler: zoomInMax,
      },
    ]

    if (isLine() && this.contractForChart) {
      const chartDiv = document.getElementById('trade-chart0')
      if (chartDiv) {
        chartDiv.dispatchEvent(new Event('zoom-in-max'))
      }
    }
    ReactDOM.render(
      <BinaryChart
      className="trade-chart"
      id="trade-chart0"
      contract={isLine() ? this.contractForChart : false}
      hideZoomControls={isLine() && this.contractForChart}
      pipSize={info.pipSize}
      shiftMode={this.contractForChart ? 'dynamic' : 'fixed'}
      ticks={info[chartToDataType[this.chartType]]}
      type={this.chartType}
      events={events}
      hideIntervalPicker
      onTypeChange={(type) => (this.chartType = type)}
      />, $('#chart')[0])
  }
  addEventHandlers() {
    for (const errorType of ['api.error', 'BlocklyError', 'RuntimeError']) {
      observer.register(errorType, (error) => { // eslint-disable-line no-loop-func
        if (error.error && error.error.code === 'InvalidToken') {
          removeAllTokens()
          this.updateTokenList()
        }
        window.Bot.stop()
        if (window.Bot.shouldRestartOnError()) {
          ReactDOM.render(
            <RestartTimeout
            timeout="3"
            />
            , document.getElementById('restartTimeout'))
        }
      })
    }

    observer.register('bot.stop', () => {
      $('#runButton').show()
      $('#stopButton').hide()
    })

    observer.register('bot.tradeInfo', (tradeInfo) => {
      for (const key of Object.keys(tradeInfo)) {
        this.tradeInfo.tradeInfo[key] = tradeInfo[key]
      }
      this.tradeInfo.update()
    })

    observer.register('bot.tradeUpdate', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = {
        ...contract,
      }
      this.contractForChart.date_expiry = Number(this.contractForChart.date_expiry)
      this.contractForChart.date_settlement = Number(this.contractForChart.date_settlement)
      this.contractForChart.date_start = Number(this.contractForChart.date_start)
    })

    observer.register('bot.finish', (contract) => {
      this.tradeInfo.add(contract)
      this.contractForChart = false
    })

    observer.register('bot.tickUpdate', (info) => {
      this.updateChart(info)
    })
  }
}
<|endoftext|>


<commit_before>const gulp            = require('gulp');
const gutil           = require("gulp-util");
const babel           = require('gulp-babel');
{% if (scripts === 'webpack') { %}
const webpack         = require('webpack-stream');
{% } else { %}
const concat          = require('gulp-concat');
{% } %}{% if (styles === 'css') { %}
const postcss         = require('gulp-postcss');
const postcssImport   = require('postcss-import');
const postcssMixins   = require('postcss-mixins');
const postcssCssnext  = require('postcss-cssnext');
const postcssComments = require('postcss-discard-comments');
const postcssScss     = require('postcss-scss');
const postcssReporter = require('postcss-reporter');
{% } else { %}
const sass            = require('gulp-sass');
const autoprefixer    = require('gulp-autoprefixer');
{% } %}
const sourcemaps      = require('gulp-sourcemaps');
const cssnano         = require('gulp-cssnano');
const rename          = require('gulp-rename');
const uglify          = require('gulp-uglify');
const jshint          = require('gulp-jshint');
const banner          = require('gulp-banner');
const manifest        = require('gulp-manifest');
const modernizr       = require('gulp-modernizr');
const imagemin        = require('gulp-imagemin');
const htmlmin         = require('gulp-htmlmin');
const notify          = require('gulp-notify');
const iconfont        = require('gulp-iconfont');
const consolidate     = require('gulp-consolidate');

const pkg             = require('./package.json');

const browserSync     = require('browser-sync').create();
const reload          = browserSync.reload;

const comment         = `/*!
 * ${pkg.name}
 *
 * Made with ❤ by ${pkg.author}
 *
 * Copyright (c) ${(new Date()).getFullYear()} ${pkg.copyright}
 */
`;

const src             = {
  cssAll:       'assets/css/_src/**/*.{%= styles %}',
  cssMain:      'assets/css/_src/main.{%= styles %}',
  cssDest:      'assets/css',
  jsAll:        'assets/js/_src/**/*.js',
  jsMain:       'assets/js/_src/main.js',
  jsDest:       'assets/js',
  iconsAll:     'assets/icons/*.svg',
  iconsCss:     'assets/icons/_template/_icons.css',
  iconsCssDest: 'assets/css/_src/partials/modules/',
  iconsDest:    'assets/fonts',
};

const uglifyConfig    = {
  mangle: {
    except: ['jQuery, u']
  },
  compress: {
    drop_console: true
  },
  preserveComments: false,
};

const prefixConfig    = {
  diff: true,
  map: false,
  remove: false,
};

const jshintConfig    = require('./.config/jshint.config');
{% if (scripts === 'webpack') { %}const webpackConfig   = require('./.config/webpack.config');{% } %}

gulp.task('watch', () => {
  browserSync.init({
    proxy: '{%= local %}',
    port: 3000,
    open: true,
    notify: false,
  });

  gulp.watch(src.cssAll, ['css']);
  gulp.watch(src.jsAll, ['js']);
  gulp.watch(src.iconsAll, ['iconfont']);
});
{% if (styles === 'css') { %}
gulp.task('css', () => {
  return gulp.src(src.cssMain)
    // .pipe(sourcemaps.init())
    .pipe(postcss([
      postcssImport,
      postcssMixins,
      postcssCssnext(prefixConfig),
      postcssComments({removeAll: true}),
      postcssReporter({ clearMessages: true }),
    ], {syntax: postcssScss}))
    // .pipe(sourcemaps.write())
    .pipe(rename('bundle.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(cssnano({
      discardComments: {
        removeAll: true
      }
    }))
    .pipe(rename('bundle.min.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(reload({stream: true}))
    .pipe(notify('css done'));
});
{% } else { %}
gulp.task('css', () => {
  return gulp.src(src.cssMain)
    // .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer(prefixConfig))
    // .pipe(sourcemaps.write())
    .pipe(rename('bundle.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(cssnano({
      discardComments: {
        removeAll: true
      }
    }))
    .pipe(rename('bundle.min.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(reload({stream: true}))
    .pipe(notify('css done'));
});
{% } %}{% if (scripts === 'webpack') { %}
gulp.task('js', ['jshint'], () => {
  return gulp.src(src.jsMain)
    .pipe(webpack(webpackConfig))
    .pipe(rename('bundle.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('bundle.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(reload({stream: true}))
    .pipe(notify('js done'));
});
{% } else { %}
gulp.task('js', ['jshint'], () => {
  return gulp.src([
      'assets/js/_src/components/cssevents.js',
      'bower_components/ujs/dist/u.js',
      'bower_components/fastclick/lib/fastclick.js',
      'bower_components/httprom/dist/http.js',
      'assets/js/_src/main.js',
      'assets/js/_src/components/social.js',
      'assets/js/_src/components/log.js',
    ])
    .pipe(babel({
      presets: ['es2015', 'stage-0'],
    }))
    .pipe(concat('bundle.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('bundle.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(reload({stream: true}))
    .pipe(notify('js done'));
});
{% } %}
gulp.task('jshint', () => {
  return gulp.src(src.jsAll)
    .pipe(jshint(jshintConfig))
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('fallback', () =>  {
  return gulp.src('assets/js/_src/fallback.js')
    .pipe(babel({
      presets: ['es2015', 'stage-0'],
    }))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('fallback.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest));
});

gulp.task('vendor', () => {
  return gulp.src([
      'bower_components/jquery/dist/jquery*',
      'bower_components/promise-polyfill/promise.*',
    ])
    .pipe(gulp.dest('assets/js/vendor'));
});

gulp.task('modernizr', () => {
  return gulp.src([`${src.cssDest}/*.css`, `${src.jsDest}/*.js`])
    .pipe(modernizr({
      "cache": false,
      "extra" : {
        "shiv" : true,
        "printshiv" : true,
        "load" : true,
        "mq" : true,
        "cssclasses" : true
      },
      "options" : [
        "setClasses",
        "addTest",
        "html5printshiv",
        "testProp",
        "fnBind",
        "mq"
      ],
      "excludeTests": [
        "hidden"
      ],
      "parseFiles" : true,
      "crawl" : true,
      "uglify" : true,
      "matchCommunityTests" : true,
    }))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(gulp.dest(`${src.jsDest}/vendor`));
});

gulp.task('manifest', () => {
  return gulp.src(['./**/*.*'])
    .pipe(manifest({
      hash: true,
      preferOnline: true,
      network: ['*'],
      filename: 'manifest.appcache',
      exclude: [
        '*.appcache',
        '**/*.json',
        '**/_src/**/*.*',
        'webpack.config.js',
        'gulpfile.js',
        'bower_components/**/*.*',
        'node_modules/**/*.*',
      ]
     }))
    .pipe(gulp.dest('./'));
});

gulp.task('imagemin', () => {
  return gulp.src('assets/img/**/*')
    .pipe(imagemin())
    .pipe(gulp.dest('assets/img'));
});

gulp.task('htmlmin', ['public'], () => {
  return gulp.src('*.html')
    .pipe(htmlmin())
    .pipe(gulp.dest('./public'));
});

gulp.task('public', () => {
  return gulp.src([
      '**/.htaccess',
      '*.png',
      '*.ico',
      '*.txt',
      '*.appcache',
      'assets/**/*.*',
      '!assets/**/_src/*',
    ])
    .pipe(gulp.dest('./public'));
});

gulp.task('iconfont', function(){
  gulp.src(src.iconsAll)
    .pipe(iconfont({
      fontName: 'icons',
      prependUnicode: false,
      formats: ['woff2', 'woff', 'svg'],
      normalize: true,
      centerHorizontally: true,
      fontHeight: 1000 // IMPORTANT
    }))
    .on('glyphs', function(glyphs, options) {
      glyphs = glyphs.map((glyph) => {
        glyph.codepoint = glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase();
        return glyph;
      });
      gulp.src(src.iconsCss)
        .pipe(consolidate('lodash', Object.assign({}, options, {
          glyphs: glyphs,
          cssPrefix: 'icon-',
          fontPath: '../fonts/',
        })))
        .pipe(rename('_icons.{%= styles %}'))
        .pipe(gulp.dest(src.iconsCssDest));
    })
    .pipe(gulp.dest(src.iconsDest));
});

gulp.task('default', ['dist', 'watch']);

gulp.task('dev', ['iconfont', 'css', 'js', 'fallback', 'modernizr', 'watch']);

gulp.task('dist', ['iconfont', 'css', 'js', 'fallback', 'vendor', 'modernizr', 'manifest', 'imagemin'], () => {
  return gulp.src('./')
    .pipe(notify('dist done'));
});

// generic error handler
function onError(err) {
  // console.log(err.toString());
  this.emit('end');
}
<commit_msg>add some postcss plugins<commit_after>const gulp            = require('gulp');
const gutil           = require("gulp-util");
const babel           = require('gulp-babel');
{% if (scripts === 'webpack') { %}
const webpack         = require('webpack-stream');
{% } else { %}
const concat          = require('gulp-concat');
{% } %}{% if (styles === 'css') { %}
const postcss         = require('gulp-postcss');
const postcssImport   = require('postcss-import');
const postcssMixins   = require('postcss-mixins');
const postcssCssnext  = require('postcss-cssnext');
const postcssComments = require('postcss-discard-comments');
const postcssScss     = require('postcss-scss');
const postcssNested   = require('postcss-nested');
const postcssEach     = require('postcss-each');
const postcssFor      = require('postcss-for');
const postcssCond     = require('postcss-conditionals');
const postcssSimple   = require('postcss-simple-vars');
const postcssReporter = require('postcss-reporter');
{% } else { %}
const sass            = require('gulp-sass');
const autoprefixer    = require('gulp-autoprefixer');
{% } %}
const sourcemaps      = require('gulp-sourcemaps');
const cssnano         = require('gulp-cssnano');
const rename          = require('gulp-rename');
const uglify          = require('gulp-uglify');
const jshint          = require('gulp-jshint');
const banner          = require('gulp-banner');
const manifest        = require('gulp-manifest');
const modernizr       = require('gulp-modernizr');
const imagemin        = require('gulp-imagemin');
const htmlmin         = require('gulp-htmlmin');
const notify          = require('gulp-notify');
const iconfont        = require('gulp-iconfont');
const consolidate     = require('gulp-consolidate');

const pkg             = require('./package.json');

const browserSync     = require('browser-sync').create();
const reload          = browserSync.reload;

const comment         = `/*!
 * ${pkg.name}
 *
 * Made with ❤ by ${pkg.author}
 *
 * Copyright (c) ${(new Date()).getFullYear()} ${pkg.copyright}
 */
`;

const src             = {
  cssAll:       'assets/css/_src/**/*.{%= styles %}',
  cssMain:      'assets/css/_src/main.{%= styles %}',
  cssDest:      'assets/css',
  jsAll:        'assets/js/_src/**/*.js',
  jsMain:       'assets/js/_src/main.js',
  jsDest:       'assets/js',
  iconsAll:     'assets/icons/*.svg',
  iconsCss:     'assets/icons/_template/_icons.css',
  iconsCssDest: 'assets/css/_src/partials/modules/',
  iconsDest:    'assets/fonts',
};

const uglifyConfig    = {
  mangle: {
    except: ['jQuery, u']
  },
  compress: {
    drop_console: true
  },
  preserveComments: false,
};

const prefixConfig    = {
  diff: true,
  map: false,
  remove: false,
};

const jshintConfig    = require('./.config/jshint.config');
{% if (scripts === 'webpack') { %}const webpackConfig   = require('./.config/webpack.config');{% } %}

gulp.task('watch', () => {
  browserSync.init({
    proxy: '{%= local %}',
    port: 3000,
    open: true,
    notify: false,
  });

  gulp.watch(src.cssAll, ['css']);
  gulp.watch(src.jsAll, ['js']);
  gulp.watch(src.iconsAll, ['iconfont']);
});
{% if (styles === 'css') { %}
gulp.task('css', () => {
  return gulp.src(src.cssMain)
    // .pipe(sourcemaps.init())
    .pipe(postcss([
      postcssImport,
      postcssMixins,
      postcssFor,
      postcssEach,
      postcssSimple,
      postcssCond,
      postcssCssnext(prefixConfig),
      postcssNested,
      postcssComments({removeAll: true}),
      postcssReporter({ clearMessages: true }),
    ], {syntax: postcssScss}))
    // .pipe(sourcemaps.write())
    .pipe(rename('bundle.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(cssnano({
      discardComments: {
        removeAll: true
      }
    }))
    .pipe(rename('bundle.min.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(reload({stream: true}))
    .pipe(notify('css done'));
});
{% } else { %}
gulp.task('css', () => {
  return gulp.src(src.cssMain)
    // .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer(prefixConfig))
    // .pipe(sourcemaps.write())
    .pipe(rename('bundle.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(cssnano({
      discardComments: {
        removeAll: true
      }
    }))
    .pipe(rename('bundle.min.css'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.cssDest))
    .pipe(reload({stream: true}))
    .pipe(notify('css done'));
});
{% } %}{% if (scripts === 'webpack') { %}
gulp.task('js', ['jshint'], () => {
  return gulp.src(src.jsMain)
    .pipe(webpack(webpackConfig))
    .pipe(rename('bundle.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('bundle.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(reload({stream: true}))
    .pipe(notify('js done'));
});
{% } else { %}
gulp.task('js', ['jshint'], () => {
  return gulp.src([
      'assets/js/_src/components/cssevents.js',
      'bower_components/ujs/dist/u.js',
      'bower_components/fastclick/lib/fastclick.js',
      'bower_components/httprom/dist/http.js',
      'assets/js/_src/main.js',
      'assets/js/_src/components/social.js',
      'assets/js/_src/components/log.js',
    ])
    .pipe(babel({
      presets: ['es2015', 'stage-0'],
    }))
    .pipe(concat('bundle.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('bundle.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(reload({stream: true}))
    .pipe(notify('js done'));
});
{% } %}
gulp.task('jshint', () => {
  return gulp.src(src.jsAll)
    .pipe(jshint(jshintConfig))
    .pipe(jshint.reporter('jshint-stylish'));
});

gulp.task('fallback', () =>  {
  return gulp.src('assets/js/_src/fallback.js')
    .pipe(babel({
      presets: ['es2015', 'stage-0'],
    }))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(rename('fallback.min.js'))
    .pipe(banner(comment))
    .pipe(gulp.dest(src.jsDest));
});

gulp.task('vendor', () => {
  return gulp.src([
      'bower_components/jquery/dist/jquery*',
      'bower_components/promise-polyfill/promise.*',
    ])
    .pipe(gulp.dest('assets/js/vendor'));
});

gulp.task('modernizr', () => {
  return gulp.src([`${src.cssDest}/*.css`, `${src.jsDest}/*.js`])
    .pipe(modernizr({
      "cache": false,
      "extra" : {
        "shiv" : true,
        "printshiv" : true,
        "load" : true,
        "mq" : true,
        "cssclasses" : true
      },
      "options" : [
        "setClasses",
        "addTest",
        "html5printshiv",
        "testProp",
        "fnBind",
        "mq"
      ],
      "excludeTests": [
        "hidden"
      ],
      "parseFiles" : true,
      "crawl" : true,
      "uglify" : true,
      "matchCommunityTests" : true,
    }))
    .pipe(uglify(uglifyConfig).on('error', onError))
    .pipe(gulp.dest(`${src.jsDest}/vendor`));
});

gulp.task('manifest', () => {
  return gulp.src(['./**/*.*'])
    .pipe(manifest({
      hash: true,
      preferOnline: true,
      network: ['*'],
      filename: 'manifest.appcache',
      exclude: [
        '*.appcache',
        '**/*.json',
        '**/_src/**/*.*',
        'webpack.config.js',
        'gulpfile.js',
        'bower_components/**/*.*',
        'node_modules/**/*.*',
      ]
     }))
    .pipe(gulp.dest('./'));
});

gulp.task('imagemin', () => {
  return gulp.src('assets/img/**/*')
    .pipe(imagemin())
    .pipe(gulp.dest('assets/img'));
});

gulp.task('htmlmin', ['public'], () => {
  return gulp.src('*.html')
    .pipe(htmlmin())
    .pipe(gulp.dest('./public'));
});

gulp.task('public', () => {
  return gulp.src([
      '**/.htaccess',
      '*.png',
      '*.ico',
      '*.txt',
      '*.appcache',
      'assets/**/*.*',
      '!assets/**/_src/*',
    ])
    .pipe(gulp.dest('./public'));
});

gulp.task('iconfont', function(){
  gulp.src(src.iconsAll)
    .pipe(iconfont({
      fontName: 'icons',
      prependUnicode: false,
      formats: ['woff2', 'woff', 'svg'],
      normalize: true,
      centerHorizontally: true,
      fontHeight: 1000 // IMPORTANT
    }))
    .on('glyphs', function(glyphs, options) {
      glyphs = glyphs.map((glyph) => {
        glyph.codepoint = glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase();
        return glyph;
      });
      gulp.src(src.iconsCss)
        .pipe(consolidate('lodash', Object.assign({}, options, {
          glyphs: glyphs,
          cssPrefix: 'icon-',
          fontPath: '../fonts/',
        })))
        .pipe(rename('_icons.{%= styles %}'))
        .pipe(gulp.dest(src.iconsCssDest));
    })
    .pipe(gulp.dest(src.iconsDest));
});

gulp.task('default', ['dist', 'watch']);

gulp.task('dev', ['iconfont', 'css', 'js', 'fallback', 'modernizr', 'watch']);

gulp.task('dist', ['iconfont', 'css', 'js', 'fallback', 'vendor', 'modernizr', 'manifest', 'imagemin'], () => {
  return gulp.src('./')
    .pipe(notify('dist done'));
});

// generic error handler
function onError(err) {
  // console.log(err.toString());
  this.emit('end');
}
<|endoftext|>