jvon/js/jvon-angular.js

449 lines
14 KiB
JavaScript

// This is the angular controller and the code pane
//noinspection JSUnresolvedFunction
var jvonapp = angular.module("JVON", ["ngTouch", "ngSanitize"]);
jvonapp.controller('JVONController', ["$scope", "$timeout", "$interval", function ($scope, $timeout ,$interval) {
// List of available commands
$scope.commands = commands;
// The code pane
$scope.code_lines = [];
// The result pane
$scope.results = [];
// What is displayed on the screen
$scope.screen = [];
$scope.execution_started = false;
$scope.speed = 1; // 1 second interval between code
// Set language to spanish by default or load english if it's saved in the browser
if (localStorage.language == 1) {
$scope.language = 1;
$scope.strings = english_strings;
}
else {
$scope.language = 0;
$scope.strings = spanish_strings;
}
// Initialize, this variable is only set to show that the code has changd to update the display
$scope.imported_code = false;
// Set and ID
$scope.identifier = new Date().getTime().toString() + navigator.userAgent + Math.random().toString();
$scope.identifier = CryptoJS.SHA512($scope.identifier).toString();
// Change the language between english and spanish
$scope.change_language = function () {
if ($scope.language == 1) {
$scope.strings = english_strings;
localStorage.language = 1;
}
else {
$scope.strings = spanish_strings;
localStorage.language = 0;
}
};
$scope.change_speed = function () {
$timeout.cancel($scope.promise);
if ($scope.execution_started == false) {
$scope.execution_started = true;
$scope.promise = $timeout($scope.execute_line, 1);
}
else {
$scope.promise = $timeout($scope.execute_line, 1000 * $scope.speed);
}
};
$scope.paused_blink = function (enabled) {
if (enabled == true) {
$scope.blinking = true;
$scope.blink_state = false;
$scope.opacity = 1;
$scope.blink_promise = $interval($scope.blink, 20);
}
else {
$interval.cancel($scope.blink_promise);
var element = document.getElementById("pause_code");
element.style.opacity = 1;
element.style.filter = "alpha(opacity=100)";
}
};
$scope.blink = function () {
var element = document.getElementById("pause_code");
if ($scope.blink_state == false) {
if ($scope.opacity <= 0.1) {
$scope.blink_state = true;
$scope.opacity = 0.1;
}
else {
element.style.opacity = $scope.opacity;
element.style.filter = "alpha(opacity=" + $scope.opacity * 100 + ")";
$scope.opacity -= $scope.opacity * 0.1;
}
}
else {
if ($scope.opacity >= 1) {
$scope.blink_state = false;
$scope.opacity = 1;
}
else {
element.style.opacity = $scope.opacity;
element.style.filter = "alpha(opacity=" + $scope.opacity * 100 + ")";
$scope.opacity += $scope.opacity * 0.1;
}
}
};
$scope.cancel_timer = function () {
$timeout.cancel($scope.promise);
};
// Show the help window
$scope.show_help = function () {
document.getElementById("help_window").style.visibility = "visible";
document.getElementById("blur").style.visibility = "visible";
};
// Hide the help window
$scope.hide_help = function () {
if (document.getElementById("help_window").style.visibility == "visible") {
document.getElementById("help_window").style.visibility = "hidden";
document.getElementById("blur").style.visibility = "hidden";
}
};
$scope.show_input_prompt = function () {
document.getElementById("input_prompt").style.visibility = "visible";
document.getElementById("blur").style.visibility = "visible";
document.getElementById("rda").focus();
};
$scope.hide_input_prompt = function () {
document.getElementById("input_prompt").style.visibility = "hidden";
document.getElementById("blur").style.visibility = "hidden";
document.getElementById("rda").value = "";
};
$scope.disable_code_buttons = function () {
document.getElementById("add_line").disabled = true;
document.getElementById("insert_line").disabled = true;
document.getElementById("delete_line").disabled = true;
document.getElementById("clear_code").disabled = true;
document.getElementById("export_code").disabled = true;
document.getElementById("file").disabled = true;
};
$scope.enable_code_buttons = function () {
document.getElementById("add_line").disabled = false;
document.getElementById("insert_line").disabled = false;
document.getElementById("delete_line").disabled = false;
document.getElementById("clear_code").disabled = false;
document.getElementById("export_code").disabled = false;
document.getElementById("file").disabled = false;
};
// Add a new line the the code pane
$scope.add_line = function () {
var len = $scope.code_lines.length - 1;
if (len != -1) {
var line_number = parseInt($scope.code_lines[len].line) + 1;
var line = {
line: line_number.toString(),
highlighted: false,
command: $scope.commands[0],
value: ""
};
$scope.code_lines.push(line);
}
else {
$scope.code_lines = [
{
line: "1",
highlighted: false,
command: commands[0],
value: ""
}
];
}
};
$scope.insert_line = function () {
if ($scope.code_lines.length > 0) {
// Find the last highlighted line
var highlighted = null;
var i;
for (i = $scope.code_lines.length - 1; i >= 0; i--) {
if ($scope.code_lines[i].highlighted == true) {
highlighted = i;
break;
}
}
if (highlighted == null) {
highlighted = $scope.code_lines.length - 1;
}
// Add a line after the last highlighted
var new_code_lines = [];
var j = 1;
for (i = 0; i < $scope.code_lines.length; i++) {
if (highlighted == i) {
$scope.code_lines[i].line = j.toString();
new_code_lines.push($scope.code_lines[i]);
j++;
var line = {
line: j.toString(),
highlighted: false,
command: $scope.commands[0],
value: ""
};
new_code_lines.push(line);
j++;
}
else {
$scope.code_lines[i].line = j.toString();
new_code_lines.push($scope.code_lines[i]);
j++;
}
}
$scope.code_lines = new_code_lines;
}
};
// Delete the highlighted lines
$scope.delete_line = function () {
var new_code_lines = [];
var j = 1;
for (var i = 0; i < $scope.code_lines.length; i++) {
if (!$scope.code_lines[i].highlighted) {
$scope.code_lines[i].line = j.toString();
j++;
new_code_lines.push($scope.code_lines[i]);
}
}
$scope.code_lines = new_code_lines;
};
// Delete all code lines
$scope.clear_code = function () {
$scope.code_lines = [];
};
$scope.select_line = function (line_number, result) {
var line;
// If execution finished remove all highlights
if ($scope.finished == true) {
$scope.finished = false;
for (var i = 0; i < $scope.code_lines.length; i++) {
if ($scope.code_lines[i].highlighted == true) {
// Unhighlight
line = document.getElementById("line1_" + i.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line2_" + i.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line3_" + i.toString());
line.className = line.className.substring(0,line.className.length - 21);
$scope.code_lines[i].highlighted = false;
}
}
}
if ($scope.executing == true && result == true) {
if ($scope.code_lines[line_number].highlighted == false) {
// Highlight
line = document.getElementById("line1_" + line_number.toString());
line.className += " code_number_selected";
line = document.getElementById("line2_" + line_number.toString());
line.className += " code_number_selected";
line = document.getElementById("line3_" + line_number.toString());
line.className += " code_number_selected";
$scope.code_lines[line_number].highlighted = true;
}
else {
// Unhighlight
line = document.getElementById("line1_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line2_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line3_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
$scope.code_lines[line_number].highlighted = false;
}
}
else if ($scope.executing == false && result == false) {
if ($scope.code_lines[line_number].highlighted == false) {
// Highlight
line = document.getElementById("line1_" + line_number.toString());
line.className += " line_number_selected";
line = document.getElementById("line2_" + line_number.toString());
line.className += " line_number_selected";
line = document.getElementById("line3_" + line_number.toString());
line.className += " line_number_selected";
$scope.code_lines[line_number].highlighted = true;
}
else {
// Unhighlight
line = document.getElementById("line1_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line2_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
line = document.getElementById("line3_" + line_number.toString());
line.className = line.className.substring(0,line.className.length - 21);
$scope.code_lines[line_number].highlighted = false;
}
}
};
$scope.export_code = function() {
// Encrypt the data
var data = $scope.code_lines;
// Remove highlights from saved data
for (var i = 0; i < data.length; i++) {
data[i].highlighted = false;
}
var filename = document.getElementById("project_name").value;
// If there is no project name, name the file "codigo"
if (filename.trim() == "") {
filename = "codigo";
}
// Cleanup the filename and romove unapproved characters
var regex = /[^A-Za-z0-9 \-\.]/g;
filename = filename.replace(regex, "") + ".jvon";
var encrypted = $scope.encrypt(JSON.stringify(data), filename);
// Create an invisble link
var link = document.createElement("a");
link.style.display = "none";
link.setAttribute("href", 'data:application/octet-stream;charset=utf-8,' + encrypted);
link.setAttribute("download", filename);
// Add the link to the DOM
document.body.appendChild(link);
// Start the download
link.click();
// Delete the link from DOM
document.body.removeChild(link);
};
$scope.key = CryptoJS.enc.Utf8.parse("ciq3IpalFhkRSerOhNmsbjcD3dRszglG");
$scope.encrypt = function(data, filename) {
var encrypted = CryptoJS.AES.encrypt(data, $scope.key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
var encrypted2 = CryptoJS.AES.encrypt(filename, $scope.key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
var hmac = CryptoJS.HmacSHA512(encrypted.toString(), CryptoJS.SHA512($scope.key)).toString();
return $scope.identifier + hmac + encrypted + "jvonfile" + encrypted2;
};
$scope.decrypt = function(data) {
data = data.split("jvonfile");
var filename = data[1];
data = data[0];
var hmac = data.substring(128,256);
var encrypted = data.substring(256);
if (hmac != CryptoJS.HmacSHA512(encrypted, CryptoJS.SHA512($scope.key)).toString()) {
// The file is invalid or hacked
alert($scope.strings.file_load_failed);
return null;
}
else {
var decrypted = CryptoJS.AES.decrypt(encrypted, $scope.key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
var decrypted2 = CryptoJS.AES.decrypt(filename, $scope.key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding });
filename = decrypted2.toString(CryptoJS.enc.Utf8);
// Remove the .jvon extension from the filename
if (filename.substring(filename.length - 5, filename.length) == ".jvon") {
filename = filename.substring(0, filename.length - 5);
}
document.getElementById("project_name").value = filename;
$scope.identifier = data.substring(0,128);
return decrypted.toString(CryptoJS.enc.Utf8);
}
};
// Watch for changes to the code from external sources and update accordingly
$scope.$watch('imported_code', function (newValue) {
if (newValue) {
$scope.code_lines = newValue;
$scope.imported_code = false;
// Reset the select dropdowns
for (var i = 0; i < $scope.code_lines.length; i++) {
for (var j = 0; j < $scope.commands.length; j++) {
if ($scope.code_lines[i].command.name == $scope.commands[j].name) {
$scope.code_lines[i].command = $scope.commands[j];
}
}
}
}
});
// Execute code line by line with an interval
$scope.repeater = function () {
if ($scope.execution_started == false) {
$scope.execution_started = true;
$scope.promise = $timeout($scope.execute_line, 1);
}
else {
$scope.promise = $timeout($scope.execute_line, 1000 * $scope.speed);
}
};
window.onbeforeunload = function() {
// Warn the user about leaving the page
return $scope.strings.leave;
};
}]);
// This directive is used to listen for a change to the file box and read the file
jvonapp.directive("importFile", function () {
return {
restrict: "A",
link: function ($scope, element) {
element.bind('change', function (event) {
var file = event.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
// Decrypt the code and parse it
var data = $scope.decrypt(e.target.result);
$scope.imported_code = JSON.parse(data);
// Erase current lines to rebuild it from the imported file
for (var i = 0; i < $scope.code_lines.length; i++) {
if ($scope.code_lines[i].highlighted == true) {
// Remove highlights from the DOM before we load anything
$scope.finished = true;
$scope.select_line(i, true);
}
}
$scope.code_lines = [];
$scope.$apply();
};
reader.readAsText(file);
}
else {
alert($scope.strings.file_load_failed);
}
});
}
};
});