diff --git a/en.png b/en.png
new file mode 100644
index 0000000..8886464
Binary files /dev/null and b/en.png differ
diff --git a/fr.png b/fr.png
new file mode 100644
index 0000000..abad7a0
Binary files /dev/null and b/fr.png differ
diff --git a/js/jscut_fr.js b/js/jscut_fr.js
new file mode 100644
index 0000000..a0f0d7f
--- /dev/null
+++ b/js/jscut_fr.js
@@ -0,0 +1,829 @@
+// Copyright 2014 Todd Fleming
+//
+// This file is part of jscut.
+//
+// jscut is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// jscut is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with jscut. If not, see .
+
+function MiscViewModel() {
+ var self = this;
+ self.enableGoogleDrive = ko.observable(options.enableGoogleDrive);
+ self.enableDropbox = ko.observable(options.enableDropbox);
+ self.debug = ko.observable(options.debug);
+ self.debugArg0 = ko.observable(0);
+ self.debugArg1 = ko.observable(0);
+ self.saveSettingsFilename = ko.observable("settings.jscut");
+ self.loadLocalStorageFilename = ko.observable("settings.jscut");
+ self.launchChiliUrl = ko.observable(null);
+ self.saveGistDescription = ko.observable("jscut settings");
+ self.savedGistUrl = ko.observable("");
+ self.savedGistLaunchUrl = ko.observable("");
+ self.localStorageSettings = ko.observableArray([]);
+ self.loadedCamCpp = ko.observable(false);
+ self.camCppError = ko.observable("");
+}
+
+var mainSvg = Snap("#MainSvg");
+var materialSvg = Snap("#MaterialSvg");
+var contentGroup = mainSvg.group();
+contentGroup.attr("filter", mainSvg.filter(Snap.filter.contrast(.5)).attr("filterUnits", "objectBoundingBox"));
+var combinedGeometryGroup = mainSvg.g();
+var tabsGroup = mainSvg.g();
+var toolPathsGroup = mainSvg.g();
+var selectionGroup = mainSvg.g();
+var renderPath;
+
+var svgViewModel;
+var materialViewModel;
+var selectionViewModel;
+var toolModel;
+var operationsViewModel;
+var tabsViewModel;
+var gcodeConversionViewModel;
+var miscViewModel;
+
+function loadScript(path, loadedCallback, errorCallback) {
+ var done = false;
+ var script = document.createElement('script');
+
+ function handleLoad() {
+ if (!done) {
+ done = true;
+ loadedCallback();
+ }
+ }
+
+ function handleReadyStateChange() {
+ var state;
+
+ if (!done) {
+ done = true;
+ if (script.readyState === "complete")
+ loadedCallback();
+ else
+ errorCallback();
+ }
+ }
+
+ function handleError() {
+ if (!done) {
+ done = true;
+ errorCallback();
+ }
+ }
+
+ script.onload = handleLoad;
+ script.onreadystatechange = handleReadyStateChange;
+ script.onerror = handleError;
+ script.src = path;
+ document.body.appendChild(script);
+}
+
+var downloadCppStarted = false;
+var triedPaths = [];
+function downloadCpp() {
+ downloadCppStarted = true;
+ if (options.camCppPaths.length == 0) {
+ console.log('Error: nothing left to try; cam-cpp is unavailable.\n');
+ var e = "cam-cpp.js is unavailable; tried the following paths:
";
+ for (var i = 0; i < triedPaths.length; ++i)
+ e += "
')
+ var result = $("#AlertNum" + alertNum);
+ if (haveTimeout)
+ setTimeout(function () {
+ result.remove();
+ }, 5000);
+ return result;
+}
+
+Snap.load("Material.svg", function (f) {
+ materialSvg.append(f);
+ materialViewModel.materialSvg(materialSvg);
+});
+
+var tutorialAlert = null;
+var nextTutorialStep = 0;
+function tutorial(step, message) {
+ if (step >= nextTutorialStep) {
+ if (tutorialAlert != null)
+ tutorialAlert.remove();
+ tutorialAlert = showAlert("Etape " + step + ": " + message, "alert-info", false);
+ nextTutorialStep = step + 1;
+ }
+}
+
+tutorial(1, 'Ouvrir un fichier .SVG');
+
+function loadSvg(alert, filename, content) {
+ svg = Snap.parse(content);
+ contentGroup.append(svg);
+ updateSvgSize();
+ if(alert)
+ alert.remove();
+ showAlert("Chargé " + filename, "alert-success");
+ tutorial(2, 'Cliquez sur 1 ou plusieurs objets.');
+}
+
+$(document).on('change', '#choose-svg-file', function (event) {
+ var files = event.target.files;
+ for (var i = 0, file; file = files[i]; ++i) {
+ (function (file) {
+ var alert = showAlert("Chargement " + file.name, "alert-info", false);
+ var reader = new FileReader();
+ reader.onload = function (e) {
+ loadSvg(alert, file.name, e.target.result);
+ };
+ reader.onabort = function (e) {
+ alert.remove();
+ showAlert("Abandonné slisez " + file.name, "alert-danger");
+ };
+ reader.onerror = function (e) {
+ alert.remove();
+ showAlert("Erreur lisez " + file.name, "alert-danger");
+ };
+ reader.readAsText(file);
+ })(file);
+ }
+ $(event.target).replaceWith(control = $(event.target).clone(true));
+});
+
+function openSvgDropbox() {
+ Dropbox.choose({
+ success: function (files) {
+ var alert = showAlert("Chargement de " + files[0].name, "alert-info", false);
+ $.get(files[0].link, function (content) {
+ loadSvg(alert, files[0].name, content);
+ }, "text").fail(function () {
+ alert.remove();
+ showAlert("Chargement " + files[0].name + " échoué", "alert-danger");
+ });
+ },
+ linkType: "direct",
+ });
+}
+
+$("#MainSvg").click(function (e) {
+ var element = Snap.getElementByPoint(e.pageX, e.pageY);
+ if (element != null) {
+ operationsViewModel.clickOnSvg(element) || tabsViewModel.clickOnSvg(element) || selectionViewModel.clickOnSvg(element);
+ if (selectionViewModel.selNumSelected() > 0) {
+ tutorial(3, 'Cliquez sur "Créer une opération" après avoir sélectionné les objets.');
+ }
+ }
+});
+
+function makeAllSameUnit(val) {
+ "use strict";
+ materialViewModel.matUnits(val);
+ tabsViewModel.units(val);
+ toolModel.units(val);
+ gcodeConversionViewModel.units(val);
+
+ var ops = operationsViewModel.operations();
+ for (var i = 0; i < ops.length; ++i)
+ ops[i].units(val);
+}
+
+function popoverHover(obj, placement, content) {
+ $(obj).popover({
+ trigger: "hover",
+ html: true,
+ content: content,
+ container: "body",
+ placement: placement
+ });
+}
+
+popoverHover('#pxPerInch', "bottom", "Les éditeurs SVG utilisent des échelles différentes les unes et les autres; régler ceci pour permettre aux tailles de sortir correctement et de correspondre.
Inkscape 0.9x:
96
Inkscape 0.4x:
90
Adobe Illustrator:
72
CorelDRAW:
96
");
+
+popoverHover('#tabsMaxCutDepth', "right", "Les opérations de profondeur maximale peuvent être coupées lorsqu'elles passent au-dessus des onglets");
+
+popoverHover('#toolDiameter', "right", "Diamètre de l'outil. V Pocket ignore cela. Simuler GCODE ignore également Diamètre si Angle <180.");
+popoverHover('#toolAngle', "right", "Angle de coupe en V. 180 pour les fraises normales (à fond plat). V Pocket est la seule opération qui obéit à cela. Simuler GCODE obéit toujours.");
+popoverHover('#toolPassDepth', "right", "La profondeur maximale dont l'outil doit plonger à chaque passe.Utilisez une profondeur de passe plus petite pour les matériaux plus durs et une meilleure qualité.");
+popoverHover('#toolStepOver', "right", "Quelle fraction du diamètre de l'outil l'outil doit franchir à chaque fois autour d'une boucle. Les petites valeurs produisent de meilleures coupes et réduisent l'usure des outils, mais prennent plus de temps à compléter.");
+popoverHover('#toolRapidRate', "right", "La vitesse de déplacement de l'outil sans couper (dans le vide)");
+popoverHover('#toolPlungeRate', "right", "La vitesse de l'outil quand il plonge vers le bas dans le matériau");
+popoverHover('#toolCutRate', "right", "La vitesse de déplacement horizontal de l'outil pendant la coupe");
+
+popoverHover('#inputMatThickness', "top", "Quelle est l'épaisseur du matériau");
+popoverHover('#selectMatZOrigin', "top", "Qu'est-ce qui est considéré comme la position 0 sur l'axe des Z");
+popoverHover('#inputMatClearance', "top", "Quelle est la hauteur de l'outil sur le matériau. Augmentez cette valeur lorsque vous utilisez des pinces ou des vis pour fixer le matériau.");
+
+popoverHover('#inputSelMinNumSegments', "top", "Nombre minimal de segments de ligne pour convertir une courbe en. jscut effectue cette conversion lorsque vous sélectionnez un objet (il devient bleu).");
+popoverHover('#inputSelMinSegmentLength', "top", "Longueur minimale de chaque segment de ligne lors de la conversion de courbes. jscut effectue cette conversion lorsque vous sélectionnez un objet (il devient bleu).");
+
+popoverHover('#gcodeZeroLowerLeft', "top", "Modifie les valeurs de décalage X et Y de sorte que 0,0 se trouve dans le coin inférieur gauche de tous les chemins d'outils.");
+popoverHover('#gcodeZeroCenter', "top", "Modifie les valeurs de décalage X et Y de sorte que 0,0 soit au centre de tous les trajets d'outil.");
+popoverHover('#gcodeReturn00', "top", "Déplacez l'outil sur 0,0 après la dernière opération.");
+popoverHover('#gcodeOffsetX', "top", "Valeur à ajouter aux coordonnées X du gcode");
+popoverHover('#gcodeOffsetY', "top", "Valeur à ajouter aux coordonnées Y du gcode");
+popoverHover('#gcodeMinX', "top", "Coordonnée X maximale dans gcode. Si cela est hors de portée de votre machine, réglez X Offset.");
+popoverHover('#gcodeMaxX', "top", "Coordonnée X maximale dans gcode. Si cela est hors de portée de votre machine, réglez X Offset.");
+popoverHover('#gcodeMinY', "top", "Coordonnée Y maximale dans gcode. Si cela est hors de portée de votre machine, réglez Y Offset.");
+popoverHover('#gcodeMaxY', "top", "Coordonnée Y maximale dans gcode. Si cela est hors de portée de votre machine, réglez Y Offset.");
+
+var operationPopovers = {
+ opEnabled: ['top', 'Si cette opération est activée'],
+ opOperation: ['top', 'Quel type d\'opération effectuer'],
+ opGenerate: ['top', 'Générer un parcours d\'outil pour l\'opération'],
+ opShowDetail: ['top', 'Afficher les détails supplémentaires'],
+ opName: ['right', 'Nom utilisé dans les commentaires gcode'],
+ opRamp: ['right', 'Rampre progressivement le couteau au lieu de plonger vers le bas'],
+ opCombine: ['right', 'Comment combiner plusieurs objets dans cette opération'],
+ opDirection: ['right', 'Dans quelle direction le couteau doit-il se déplacer'],
+ opCutDepth: ['top', 'Quelle profondeur cette opération devrait couper au total'],
+ opVMaxDepth: ['right', "Profondeur maximale de cette opération devrait couper.
pas encore implémenté; ceci est ignoré. p>"],
+ opMargin: ['right', 'Positif: combien de matière à laisser non coupée.
Négatif: combien de matière supplémentaire à couper'],
+ opWidth: ['right', 'Quelle largeur de chemin à couper. Si cette largeur est inférieure à la largeur de la fraise, elle utilise la largeur de la fraise.'],
+}
+
+var tabPopovers = {
+ tabEnabled: ['top', 'Si cet onglet est activé'],
+ tabMargin: ['top', 'Positif: combien pour agrandir l\'onglet.
Négatif: combien pour réduire l\'onglet.'],
+}
+
+function hookupOperationPopovers(nodes) {
+ "use strict";
+ for (var i = 0; i < nodes.length; ++i) {
+ var node = nodes[i];
+ hookupOperationPopovers(node.childNodes);
+ if (node.id in operationPopovers)
+ popoverHover(node, operationPopovers[node.id][0], operationPopovers[node.id][1]);
+ }
+}
+
+function hookupTabPopovers(nodes) {
+ "use strict";
+ for (var i = 0; i < nodes.length; ++i) {
+ var node = nodes[i];
+ hookupTabPopovers(node.childNodes);
+ if (node.id in tabPopovers)
+ popoverHover(node, tabPopovers[node.id][0], tabPopovers[node.id][1]);
+ }
+}
+
+$('#createOperationButton').popover({
+ trigger: "manual",
+ html: true,
+ content: "
Sélectionnez 1 ou plusieurs objets dans l'onglet \"Modifier les parcours d'outils \" avant de cliquer ici
Avez-vous des paramètres par défaut? Nommez-les "preload.jscut" et cliquez sur "Dans le navigateur". jscut chargera automatiquement ceci à chaque démarrage.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Charger des paramètres
+
+
+
+
Selectioner les paramètres:
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Effacer les paramètres
+
+
+
+
Effacer ""?
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sauvegarder GCODE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sauvegarder GCODE
+
+
+
+ gcode enregistré sur chilipeppr.com.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Créer un gist annanyme
+
+
+
+ Warning !!: Cela va créer un Gist anonyme qui inclut vos paramètres et vos fichiers SVG.
+ Gists anonymes sont publics. Gists anonyme ne peut pas être supprimé.
+