diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..e98d284 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +*Short introduction of your problem.* + +__Input code:__ +```vb +/* The code your entered in the input textarea */ +``` + +__Output code:__ +```arduino +/* The code outputted by the script, or 'none' if no script outputted */ +``` diff --git a/Dckuino.js b/Dckuino.js deleted file mode 100644 index c511379..0000000 --- a/Dckuino.js +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Dckuino.js, an open source project licenced under MIT License - */ - -/* jshint esversion: 6 */ -/* jshint laxbreak: true */ - -var commandMap = { // Key that can be typed - ESCAPE:'KEY_ESC', - ESC:'KEY_ESC', - GUI:'KEY_LEFT_GUI', - WINDOWS:'KEY_LEFT_GUI', - COMMAND:'KEY_LEFT_GUI', - MENU:'229', - APP:'229', - END:'KEY_END', - SPACE:'\' \'', - TAB:'KEY_TAB', - PRINTSCREEN:'206', - ENTER:'KEY_RETURN', - RETURN:'KEY_RETURN', - UPARROW:'KEY_UP_ARROW', - DOWNARROW:'KEY_DOWN_ARROW', - LEFTARROW:'KEY_LEFT_ARROW', - RIGHTARROW:'KEY_RIGHT_ARROW', - UP:'KEY_UP_ARROW', - DOWN:'KEY_DOWN_ARROW', - LEFT:'KEY_LEFT_ARROW', - RIGHT:'KEY_RIGHT_ARROW', - CAPSLOCK:'KEY_CAPS_LOCK', - DELETE:'KEY_DELETE', - DEL:'KEY_DELETE', - F1:'KEY_F1', - F2:'KEY_F2', - F3:'KEY_F3', - F4:'KEY_F4', - F5:'KEY_F5', - F6:'KEY_F6', - F7:'KEY_F7', - F8:'KEY_F8', - F9:'KEY_F9', - F10:'KEY_F10', - F11:'KEY_F11', - F12:'KEY_F12', - PAGEUP:'KEY_PAGE_UP', - PAGEDOWN:'KEY_PAGE_DOWN' -}; - -var comboMap = { // Key that can only be used in combos - ALT:'KEY_LEFT_ALT', - SHIFT:'KEY_LEFT_SHIFT', - CTRL:'KEY_LEFT_CTRL', - CONTROL:'KEY_LEFT_CTRL' -}; - -var keyMap = { // Normal keys - a:'a', - b:'b', - c:'c', - d:'d', - e:'e', - f:'f', - g:'g', - h:'h', - i:'i', - j:'j', - k:'k', - l:'l', - m:'m', - n:'n', - o:'o', - p:'p', - q:'q', - r:'r', - s:'s', - t:'t', - u:'u', - v:'v', - w:'w', - x:'x', - y:'y', - z:'z' -}; - -class Dckuinojs { - constructor() { - this.keyMap = keyMap; - this.commandMap = commandMap; - this.comboMap = comboMap; - } - - toArduino(inputCode) - { - // Check if the parameter is empty or undefined - if (inputCode === '' || inputCode === undefined) - { - console.error('Error: No ducky script was entered !'); - return false; - } // Parsing - - var parsedDucky = this._parse(inputCode); - if (parsedDucky === '' || parsedDucky === undefined) - { - return false; - } // Returning the total uploadable script - - return '/*\n' - + ' * Generated with <3 by Dckuino.js, an open source project !\n' - + ' */\n\n' - + '#include "Keyboard.h"\n\n' - + 'void typeKey(uint8_t key)\n' - + '{\n' - + ' Keyboard.press(key);\n' - + ' delay(50);\n' - + ' Keyboard.release(key);\n' - + '}\n\n' - + '/* Init function */\n' - + 'void setup()\n' - + '{\n' - + ' // Begining the Keyboard stream\n' - + ' Keyboard.begin();\n\n' - + ' // Wait 500ms\n' - + ' delay(500);\n' - + '\n' + parsedDucky - + ' // Ending stream\n' - + ' Keyboard.end();\n' - + '}\n\n' - + '/* Unused endless loop */\n' - + 'void loop() {}'; - } - - // The parsing function - _parse(toParse) - { - // Init chronometer - var timerStart = Date.now(); - - // Preset all used vars - var parsedScript = ''; - var lastLines; - var lastCount; - var parsedOut = ''; - - var commandKnown = false; - var releaseAll = false; - var noNewline = false; - var noDelay = false; - var nextNoDelay = false; - - var wordArray; - var wordOne; - - // Init default delay - var defaultDelay = 0; - - // Trim whitespaces - toParse = toParse.replace(/^ +| +$/gm, ''); - - // Remove all *ugly* tabs - toParse = toParse.replace(/\t/g, ''); - - // Cut the input in lines - var lineArray = toParse.split('\n'); - - // Loop every line - for (var i = 0; i < lineArray.length; i++) - { - // Line empty, skip - if (lineArray[i] === '' || lineArray[i] === '\n') - { - console.log('Info: Skipped line ' + (i + 1) + ', because was empty.'); - continue; - } - - // Outputs, for REPLAY/REPEAT COMMANDS - if (parsedOut !== undefined && parsedOut !== '') - { - lastLines = parsedOut; - lastCount = ((lastLines.split('\n')).length + 1); - } - - // Reset line buffer - parsedOut = ''; - - // Set to unknown command by default - commandKnown = false; - - // releaseAll & noNewline & noDelay; *Line Modifiers* - releaseAll = false; - noNewline = false; - noDelay = nextNoDelay; - nextNoDelay = false; - - // Cut every line in words & store the first word in a var - wordArray = lineArray[i].split(' '); - wordOne = wordArray[0]; - - // Parse commands - switch(wordOne){ - case "STRING": - wordArray.shift(); - - var textString = wordArray.join(' '); - - // Replace all '"' by '\"' and all '\' by '\\' - textString = textString.split('\\').join('\\\\').split('"').join('\\"'); - if (textString !== '') - { - parsedOut = ' Keyboard.print(F("' + textString + '"));\n'; - commandKnown = true; - } else { - console.error('Error: at line: ' + (i + 1) + ', STRING needs a text'); - return; - } - break; - case "DELAY": - wordArray.shift(); - - if(wordArray[0] === undefined || wordArray[0] === '') { - console.error('Error: at line: ' + (i + 1) + ', DELAY needs a time'); - return; - } - - if (! isNaN(wordArray[0])) - { - parsedOut = ' delay(' + wordArray[0] + ');\n'; - commandKnown = true; noDelay = true; nextNoDelay = true; - } else { - console.error('Error: at line: ' + (i + 1) + ', DELAY only acceptes numbers'); - return; - } - break; - case "DEFAULT_DELAY": - wordArray.shift(); - - if(wordArray[0] === undefined || wordArray[0] === '') { - console.error('Error: at line: ' + (i + 1) + ', DEFAULT_DELAY needs a time'); - return; - } - - if (! isNaN(wordArray[0])) - { - defaultDelay = wordArray[0]; - commandKnown = true; noNewline = true; noDelay = true; - } else { - console.error('Error: at line: ' + (i + 1) + ', DEFAULT_DELAY only acceptes numbers'); - return; - } - break; - case "TYPE": - wordArray.shift(); - - if(wordArray[0] === undefined || wordArray[0] === '') { - console.error('Error: at line: ' + (i + 1) + ', TYPE needs a key'); - return; - } - - if (keyMap[wordArray[0]] !== undefined) - { - commandKnown = true; - // Replace the DuckyScript key by the Arduino key name - parsedOut = ' typeKey(\'' + keyMap[wordArray[0]] + '\');\n'; - } else { - console.error('Error: Unknown letter \'' + wordArray[0] +'\' at line: ' + (i + 1)); - return; - } - break; - case "REM": - wordArray.shift(); - - // Placing the comment to arduino code - if (wordArray.length > 0) - { - commandKnown = true; noDelay= true; - parsedOut = ' // ' + wordArray.join(' '); - if (i == (lineArray.length - 1)) - parsedOut += '\n'; - } else { - console.error('Error: at line: ' + (i + 1) + ', REM needs a comment'); - return; - } - break; - case "REPEAT": - case "REPLAY": - wordArray.shift(); - - if (wordArray[0] === undefined || wordArray[0] === '') { - console.error('Error: at line: ' + (i + 1) + ', REPEAT/REPLAY needs a loop count'); - return; - } - - if (lastLines === undefined) - { - console.error('Error: at line: ' + (i + 1) + ', nothing to repeat, this is the first line.'); - return; - } - - if (! isNaN(wordArray[0])) - { - // Remove the lines we just created - var linesTmp = parsedScript.split('\n'); - linesTmp.splice(-lastCount, lastCount); - - if (linesTmp.join('\n') === '') - parsedScript = linesTmp.join('\n'); - else { - parsedScript = linesTmp.join('\n') + '\n'; - } - - // Add two spaces at Begining - lastLines = lastLines.replace(/^ /gm,' '); - - // Replace them - parsedOut = ' for(int i = 0; i < ' + wordArray[0] + '; i++) {\n'; - parsedOut += lastLines; - parsedOut += ' }\n'; - - commandKnown = true; noDelay = true; - } else { - console.error('Error: at line: ' + (i + 1) + ', REPEAT/REPLAY only acceptes numbers'); - return; - } - break; - default: - if (wordArray.length == 1) - { - if (comboMap[wordArray[0]] !== undefined) - { - commandKnown = true; - - parsedOut = ' typeKey(' + comboMap[wordArray[0]] + ');\n'; - }else if (commandMap[wordArray[0]] !== undefined) { - commandKnown = true; - - parsedOut = ' typeKey(' + commandMap[wordArray[0]] + ');\n'; - }else { - commandKnown = false; - break; - } - wordArray.shift(); - } - while (wordArray.length){ - if (comboMap[wordArray[0]] !== undefined) - { - commandKnown = true; - releaseAll = true; - - parsedOut += ' Keyboard.press(' + comboMap[wordArray[0]] + ');\n'; - }else if (commandMap[wordArray[0]] !== undefined) { - commandKnown = true; - releaseAll = true; - - parsedOut += ' Keyboard.press(' + commandMap[wordArray[0]] + ');\n'; - }else if (keyMap[wordArray[0]] !== undefined) { - commandKnown = true; - releaseAll = true; - - parsedOut += ' Keyboard.press(\'' + keyMap[wordArray[0]] + '\');\n'; - }else { - commandKnown = false; - break; - } - wordArray.shift(); - } - } - - if (!commandKnown) - { - console.error('Error: Unknown command or key \'' + wordArray[0] + '\' at line: ' + (i + 1) + '.'); - return; - } - - // If we need to release keys, we do - if (releaseAll) - parsedOut += ' Keyboard.releaseAll();\n'; - - // If there is a default delay add it - if (defaultDelay > 0 && !noDelay) - parsedOut = ' delay(' + defaultDelay + ');\n\n' + parsedOut; - - parsedScript += parsedOut; // Add what we parsed - - if (!noNewline) - parsedScript += '\n'; // Add new line - } - - var timerEnd = Date.now(); - var timePassed = new Date(timerEnd - timerStart); - - console.log('Done parsed ' + (lineArray.length) + ' lines in ' + timePassed.getMilliseconds() + 'ms'); - return parsedScript; - } -} diff --git a/Duckuino.js b/Duckuino.js new file mode 100644 index 0000000..fc7b703 --- /dev/null +++ b/Duckuino.js @@ -0,0 +1,243 @@ +/** + * Duckuino, an open source project licenced under MIT License + * GitHub repo can be found at the following link: + * - https://github.com/Nurrl/Duckuino + */ + +/* jshint esversion: 6 */ + +/* Function to request files */ +function getFile(sUrl) { + /* Init request */ + var oReq = new XMLHttpRequest(); + + /* Sending request */ + oReq.open("get", sUrl, false); + oReq.overrideMimeType("text/plain"); + oReq.send(null); + + /* Getting response */ + if (oReq.readyState == 4 && (oReq.status == 200 || oReq.status === 0)) { + return oReq.responseText; + } else { + return undefined; + } +} + +class Duckuino { + constructor() {} + + listModules() { + /* List all modules in the moduleList file */ + if (!this.moduleArray) { + this.moduleArray = getFile("modules/modules").split('\n'); + this.moduleArray.pop(); + } + + /* Return the list */ + return this.moduleArray; + } + + loadModule(moduleName) { + /* Check if module exists */ + if (this.listModules().indexOf(moduleName) == -1) { + console.error("Error: This module doesn't exist !"); + + /* Module is not loaded */ + this.loadedModule = undefined; + } else { + /* Load module *//* jshint evil:true */ + this.loadedModule = eval(getFile("modules/" + moduleName + ".js")); + } + } + + /* TO-DO: getModuleInfos() {} */ + + compileCode(compileStr) { + /* Init timer */ + var timerStart = window.performance.now(); + + /* Check if module loaded */ + if (this.loadedModule === undefined) { + return { + compiledCode: undefined, + compileTime: -1, + + returnCode: 9, + returnMessage: "Error: No module loaded !", + errorList: this.errorList + }; + } + + /* Check if code is empty */ + if (compileStr === undefined || compileStr === "") { + return { + compiledCode: undefined, + compileTime: -1, + + returnCode: 9, + returnMessage: "Error: No input was entered !", + errorList: this.errorList + }; + } + + /* Trim whitespaces and tabs */ + compileStr = compileStr.replace(/^ +| +$/gm, ''); + compileStr = compileStr.replace(/\t/g, ''); + + /* Errors */ + this.errorList = []; + + /* Preset all used vars */ + this.dataStorage = new Object(); + this.compiledCode = ''; + + var commandKnown; + var lineStr; + var lastLine = ''; var lastLineCount = 0; + + /* Cut the input in lines */ + var lineArray = compileStr.split('\n'); + + /* Loop every line */ + for (var i = 0; i < lineArray.length; i++) + { + /* Line empty, skip */ + if (lineArray[i] === '' || lineArray[i] === '\n') + continue; + + /* Reset vars */ + commandKnown = false; + lineStr = ''; + + /* Split lines in words */ + var argList = lineArray[i].split(' '); + var argOne = argList[0]; + + /* Parse commands */ + if (this.loadedModule.functionMap[argOne] !== undefined) { + var µ = new Object({ + keyMap: this.loadedModule.keyMap, + /** + * Pushes the error to the global error list. + */ + throwError: function(thisPtr, currentLine) { + return function(errorMessage) { + thisPtr.errorList.push({ + errorMessage: errorMessage, + errorLine: currentLine + }); + }; + }(this, (i + 1)), + /** + * This one is to get the lastLine, and in the same way trim + * it from from the current compiledCode, this is a workaround for + * the REPLAY command. + */ + trimLast: function(thisPtr, lastLine, lastLineCount) { + return function() { + var tmpVar = thisPtr.compiledCode.split('\n'); + + tmpVar.splice(-lastLineCount, lastLineCount - 1); + thisPtr.compiledCode = tmpVar.join('\n'); + + return lastLine; + }; + }(this, lastLine, lastLineCount), + /** + * Those two function are used to store persistent data, i.e: + * Default Delay. + */ + setData: function(thisPtr) { + return function(dataName, dataValue) { + thisPtr.dataStorage[dataName] = dataValue; + }; + }(this), + getData: function(thisPtr) { + return function(dataName) { + return thisPtr.dataStorage[dataName]; + }; + }(this), + }); + + /* Execute the function and add the returned string to the current string */ + lineStr += this.loadedModule.functionMap[argOne](argList, µ); + + /* Post process the line */ + lineStr = this.loadedModule.postLine(lineStr, µ); + } else { /* Parse keystokes */ + var strokeArray = Array(); + + for(var y = 0; y < argList.length; y++) { + + if(this.loadedModule.commandMap[argList[y]] !== undefined) { + /* Push key to Array */ + strokeArray.push(this.loadedModule.commandMap[argList[y]]); + } else if(this.loadedModule.comboMap[argList[y]] !== undefined) { + /* Push key to Array */ + strokeArray.push(this.loadedModule.comboMap[argList[y]]); + } else if(this.loadedModule.keyMap[argList[y]] !== undefined && y != 0) { + /* Push key to Array */ + strokeArray.push('"' + this.loadedModule.keyMap[argList[y]] + '"'); + } else { + /* If command unknown, throw error */ + this.errorList.push({ + errorMessage: "Unknown command or key: '" + argList[y] + "'", + errorLine: (i + 1) + }); + } + } + + /* Transform key array to string */ + lineStr += this.loadedModule.computeKeys(strokeArray); + } + + /* Calculate line count */ + lastLineCount = lineStr.split('\n').length; + + /* Append this compiled line to global output */ + lastLine = lineStr; + this.compiledCode += lineStr; + } + + /* Stop timer */ + var timerEnd = window.performance.now(); + var timeElapsed = (timerEnd - timerStart).toFixed(2); + + /* Return error if error and code if not */ + if (this.errorList.length > 0) { + /* Return error(s) */ + return { + compiledCode: undefined, + compileTime: -1, + + returnCode: 1, + returnMessage: function(errorList) { + var errorString; + + if(errorList.length > 1) { + errorString = "The compiler returned some errors:\n"; + } else { + errorString = "The compiler returned an error:\n"; + } + + errorList.forEach(function(errorObj) { + errorString += "Line " + errorObj.errorLine + " -> " + errorObj.errorMessage + "\n"; + }); + + return errorString; + }(this.errorList), + errorList: this.errorList + }; + } else { + /* Return the compiled code */ + return { + compiledCode: this.loadedModule.getFinalCode(this.compiledCode), + compileTime: timeElapsed, + + returnCode: 0, + returnMessage: "Info: Code successfully parsed in " + timeElapsed + "ms !" + }; + } + } +} diff --git a/LICENSE b/LICENSE index 00c0027..21ae38f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Nurrl +Copyright (c) 2016-2017 Nurrl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 07257e0..5b5e1a2 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,27 @@ -# Duckuino +# Duckuino ![release](https://img.shields.io/github/release/Nurrl/Duckuino/all.svg) Simple DuckyScript to Arduino converter. If you need to perform mouse emulation then use [d4n5h's Duckuino](https://github.com/d4n5h/Duckuino). *NOTE: If you are on linux, you might use the Arduino IDE from the website, not from apt, because the apt repo is not up to date.* -# Live version: +### Warning: Release note +This release is an unstable version, for now some things may or may not work, please [*open an issue*](https://github.com/Nurrl/Duckuino/issues/new) if you find a bug. + +*The stable version will be released in few days, maybe :D* +## Live version: https://nurrl.github.io/Duckuino/ -# Why Duckuino ? -You can compile duckyscript to arduino code directly through the [live](https://nurrl.github.io/Duckuino/ "Duckuino Live") version, or reuse Dckuino.js for standalone use : +## Why Duckuino ? +You can compile **Duckyscript** to **Arduino** code directly through the [live](https://nurrl.github.io/Duckuino/ "Duckuino Live") version, or reuse Duckuino.js for standalone use : ```javascript -// Create the instance -Duck = new Dckuinojs(); - -var DuckyScript = "CTRL ALT t\n" -+ "DELAY 1000\n" -+ "STRING gedit\n" -+ "ENTER\n" -+ "DELAY 1000\n" -+ "STRING Hello World !" - -var ArduinoCode = Duck.toArduino(DuckyScript); - -console.log(ArduinoCode); +/* Need to fill */ ``` Output: ```c -/* - * Generated with <3 by Dckuino.js, an open source project ! - */ - -#include - -void typeKey(int key) -{ - Keyboard.press(key); - delay(50); - Keyboard.release(key); -} - -// Init function -void setup() -{ - // Begining the stream - Keyboard.begin(); - - // Waiting 500ms for init - delay(500); - - Keyboard.press(KEY_LEFT_CTRL); - Keyboard.press(KEY_LEFT_ALT); - Keyboard.press(116); - Keyboard.releaseAll(); - - delay(1000); - - Keyboard.print("gedit"); - - typeKey(KEY_RETURN); - - delay(1000); - - Keyboard.print("Hello World !"); - // Ending stream - Keyboard.end(); -} - -// Unused -void loop() {} +/* Need to fill */ ``` # Members - [Plazmaz](https://github.com/Plazmaz) - [Nurrl](https://github.com/Nurrl) - diff --git a/assets/css/style.css b/assets/css/style.css index d3c7682..7c4e0fd 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -10,135 +10,211 @@ html, body { font-family: Lato, sans-serif; font-size: 16px; - /* Scroll */ + /* Disable Scroll */ overflow: hidden; - /* Background */ - background: #333333; - /* Size */ height: 100%; width: 100%; } -menu { +/* Background blured image */ +body::before { /* Position */ - position: relative; - top: 0; + position: absolute; + top: 0; left: 0; - /* Color */ - background: #f6f6f6; + /* Size */ + height: 100%; width: 100%; - /* Size and spacing */ - margin: 0; padding: 0; - width: 100%; height: 48px; + /* Background */ + background-size: cover; + background-image: url(../imgs/background.jpg); + background-repeat: no-repeat; + background-position: center; + + /* Misc */ + /*filter: blur(1px);*/ + content: ""; + z-index: -1; +} - /* Prepare underligning */ - padding-bottom: 2px; +/* Text */ +h1 { + margin: 0; +} +h6 { + margin: 0; + opacity: .7; +} - /* Text style overrride */ - font-size: 16px; +/* Textareas */ +textarea { + width: 100%; height: 50vh; + border: none; border-radius: 4px; + padding: 8px; - /* Overflow */ - overflow: hidden; + box-sizing: border-box; + resize: none; } -menu .left {float: left;} -menu .right {float: right;} -menu a { - /* Position */ - display: block; - padding: 0 16px; - /* Height */ - height: 48px; +/* Buttons */ +button { + /* Style */ + color: black; + cursor: pointer; + background-color: white; + border: 1px solid white; + + /* Size */ + width: 128px; + height: 32px; - /* Text styling */ - text-decoration: none; - text-align: center; - line-height: 48px; - color: #333333; + /* Animation */ + transition: .2s; } -menu a.hoverable:hover { - /* On hover */ - background: #eeeeee; - box-shadow: 0 2px 0 0 #333333; +button:not(:disabled):hover { + /* Style */ + background-color: transparent; + color: white; - /* Cursor style */ - cursor: pointer; + /* Animation */ + transition: .2s; } +button:disabled { + /* Style */ + color: white; + background-color: transparent; -.inout-wrapper { - position: relative; - margin: 16px; + /* Back pattern */ + background-size: 32px 32px; + background-image: linear-gradient(135deg, rgba(255, 255, 255, 1) 25%, transparent 25%, + transparent 50%, rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 1) 75%, + transparent 75%, transparent); + + cursor: default; - width: 100%; height: calc(100% - 38px - 50px - 48px); /* 100% - console - menu - margin */ + /* Opacity */ + opacity: .5; } -.inout { - border: 1px solid #f6f6f6; - border-radius: 3px; - background: #f6f6f6; - color: #333333; +/* Top bar */ +.menu { + position: absolute; + top: 0; - resize: none; + width: 100%; +} +.menu a { + float: right; + margin: 16px; + + text-transform: uppercase; + text-decoration: none; + color: white; - padding: 2px; - width: calc(50% - 16px - 8px); - height: 100%; + transition: .2s; +} +.menu a:hover { + transition: .2s; + color: black; } -.inout .duckyscript {float: left;} -.inout .arduino {float: right;} -.console { - border: 1px solid #f6f6f6; - border-radius: 3px; +/* Page organization */ +.group { + /* Size */ + width: 100%; height: 100%; - background: #f6f6f6; - color: #333333; + /* Center parts */ + display: flex; +} - overflow: hidden; - resize: none; - +.part { + /* Position */ + margin: 0 1.5%; padding: 16px; + align-self: center; + width: 30%; + float: left; + + box-sizing: border-box; + border-radius: 4px; + + /* Separation */ + /*box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);*/ + + /* Text */ + color: white; +} + +/* Elements */ +.process { + /* Center text */ + text-align: center; +} + +/* Info/error tooltip */ +.tooltip { position: relative; - - padding: 2px; margin: 8px 16px; - width: calc(100% - 32px - 8px); /* Wrapper width - (margin + padding + textarea borders) */ - height: 32px; - line-height: 32px; + width: 100%; + + display: none; } +.tooltip > span { + /* Size */ + position: absolute; + width: 90%; -/* The download popup */ -.modal { - display: none; /* Hidden by default */ - position: fixed; /* Stay in place */ - z-index: 1; /* Sit on top */ - left: 0; - top: 0; - width: 100%; /* Full width */ - height: 100%; /* Full height */ - overflow: auto; /* Enable scroll if needed */ - background-color: rgb(0,0,0); /* Fallback color */ - background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ + /* Position */ + left: 50%; + transform: translateX(-50%); + margin-top: 12px; + z-index: -1; + + /* Style */ + text-align: left; + border-radius: 4px; + white-space: pre-wrap; + padding: 4px; + color: white; +} +.tooltip > span::before { + content: ""; + position: absolute; + bottom: 100%; + left: 50%; + margin-left: -8px; + border-width: 8px; + border-style: solid; } +.tooltip.info > span {background-color: #029bc9;} +.tooltip.info > span::before {border-color: transparent transparent #029bc9 transparent;} -/* Popup Content/Box */ -.modal-content { - background-color: #fefefe; - margin: 15% auto; /* 15% from the top and centered */ - padding: 20px; - border: 1px solid #888; - width: 80%; /* Could be more or less, depending on screen size */ +.tooltip.error > span {background-color: #cc0000;} +.tooltip.error > span::before {border-color: transparent transparent #cc0000 transparent;} + +.combined-but > button { + width: 112px; + border-right: 0; } +.combined-but > select { + /* Postion */ + margin-left: -4px; -/* The Close Button */ -.close { - color: #aaa; - float: right; - font-size: 28px; - font-weight: bold; + /* Style */ + color: transparent; + border: 1px solid white; + border-left: 0; + + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + background: url(../imgs/arrow-down.png) no-repeat white center; + + /* Size */ + width: 16px; + height: 32px; } -.close:hover, -.close:focus { - color: black; - text-decoration: none; - cursor: pointer; +.combined-but > select:disabled { + /* Postion */ + opacity: .5; } diff --git a/assets/imgs/arrow-down.png b/assets/imgs/arrow-down.png new file mode 100644 index 0000000..726f415 Binary files /dev/null and b/assets/imgs/arrow-down.png differ diff --git a/assets/imgs/background.jpg b/assets/imgs/background.jpg new file mode 100644 index 0000000..dc0e19c Binary files /dev/null and b/assets/imgs/background.jpg differ diff --git a/assets/js/main.js b/assets/js/main.js index 117cc06..505974a 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,86 +1,82 @@ -jQuery(function() { // Wait for jQuery +$(function() { /* Wait for jQuery */ - init(); + /* Init vars */ + var isCodeCompiled = false; + var LocKey = new LocaleKeyboard(); + var Duck = new Duckuino(); - // Check if download button can be used try { var isFileSaverSupported = !!new Blob(); } catch (e) {} - // Hijack console.log and console.error - (function(){ - var oldLog = console.log; - console.log = function (message) { - $(".console").val(" ℹ - " + message + '\n'); - $(".console").css("color", "#2b2b2b"); - oldLog.apply(console, arguments); - }; - var oldErrorLog = console.error; - console.error = function (message) { - $(".console").val(" ⚠ - " + message + '\n'); - $(".console").css("color", "#ff3434"); - oldErrorLog.apply(console, arguments); - }; - })(); - - // Create a little duck translator - Duck = new Dckuinojs(); - - // Compile button - $(".compile-but").click(function(e) { - var duckOutput = Duck.toArduino($(".duckyscript").val()); - - if (duckOutput !== false) - { - $(".arduino").val(duckOutput); - if (isFileSaverSupported) - enableDl(500); // Enable download button - } - else { - $(".arduino").val('An error occured, the compiler returned undefined content !'); - disableDl(500); // Disable download button + /* Compile button enable/disable */ + $(".input > textarea").keyup(function() { + if($(this).val() !== "") { + $(".process-but button").prop("disabled", false); + $(".process-but select").prop("disabled", false); + } else { + $(".process-but button").prop("disabled", true); + $(".process-but select").prop("disabled", true); } }); - // Generate locale list - var LocaleKeyboardjs = new LocaleKeyboard(); - - // Download popup - $(".dl-but").click(function() { - if ($(".dl-but").hasClass("hoverable")) { - $("#dl-popup").fadeIn(400); - - // Fill filename area - $("#dl-filename").val("Dckuino.js-" + makeId(4)); - - // Clear locale list - $('#locale-select').find('option').remove(); - - // Fill locale list - $(LocaleKeyboardjs.listLocales()).each(function() { - $("#locale-select").append($(""); }); - // Download button - $("#start-dl").click(function() { - // Check if all is ready - if ($("#dl-filename").val() === "") { - alert("You must enter a filename"); - return; - } + /* List modules */ + Duck.listModules().forEach(function (moduleName) { + $(".process-but select").append(""); + }); + + /* Download button */ + $(".dl-but button").click(function() { + var compilerOut = $(".export > textarea").val(); - // Create a zip and download - var sketchName = $("#dl-filename").val(); + var sketchName = "Sketch"; var zipHandler = new JSZip(); // Add the payload as .ino - zipHandler.file(sketchName + "/" + sketchName + ".ino", $(".arduino").val()); + zipHandler.file(sketchName + "/" + sketchName + ".ino", compilerOut); + // Add readme zipHandler.file("readme", $.ajax({ url: 'readme.default', @@ -90,13 +86,13 @@ jQuery(function() { // Wait for jQuery })); // Add custom version of Keyboard lib if needed - if ($("#locale-select").find(":selected").text() !== "en_US") { + if ($(".export-but select").find(":selected").text() !== "en_US") { // Set the locale - LocaleKeyboardjs.setLocale($("#locale-select").find(":selected").text()); + LocKey.setLocale($(".dl-but select").find(":selected").text()); // Append all to the zip - zipHandler.file(sketchName + "/Keyboard.cpp", LocaleKeyboardjs.getSource()); - zipHandler.file(sketchName + "/Keyboard.h", LocaleKeyboardjs.getHeader()); + zipHandler.file(sketchName + "/Keyboard.cpp", LocKey.getSource()); + zipHandler.file(sketchName + "/Keyboard.h", LocKey.getHeader()); } // Download @@ -106,36 +102,17 @@ jQuery(function() { // Wait for jQuery } ); }); -}); - -function init() -{ - // Init page - - // Disable download button by default - disableDl(500); - - // Clear console - $(".console").val(""); -} -function disableDl(time) { - $(".dl-but").addClass("disabled").removeClass("hoverable"); - $(".dl-but span i.fa-ban").fadeTo(time, 1); -} + /* Copy to clipboard button */ + $(".copy-but").click(function() { + var copyTextarea = $(".export > textarea"); + copyTextarea.select(); -function enableDl(time) { - $(".dl-but").removeClass("disabled").addClass("hoverable"); - $(".dl-but span i.fa-ban").fadeTo(time, 0); -} + try { + document.execCommand('copy'); -function makeId(idLenght) -{ - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for( var i=0; i < idLenght; i++ ) - text += possible.charAt(Math.floor(Math.random() * possible.length)); - - return text; -} + $(".copy-but").text("Copied !"); + $(".copy-but").prop("disabled", true); + } catch (e) {/* Error */} + }); +}); diff --git a/index.html b/index.html index b8358f4..5e58835 100644 --- a/index.html +++ b/index.html @@ -3,14 +3,6 @@ - - - - - - - - @@ -19,56 +11,68 @@ - Dckuino.js + Duckuino - -